01 · THREAD iOS e Flutter · 2019 → hoje
O frame tem dezesseis milissegundos
Toda discussão de concorrência num time mobile é, no fundo, sobre um número só. Uma tela deve um frame a cada 16,7 ms, e tudo — async/await, actors, isolates, cancelamento — é sobre o que você pode fazer dentro dele. Abaixo está a mesma lista carregada de três jeitos, com o gráfico de frames que um profiler mostraria.
Uma tela deve um frame a cada 16,7 ms. Esta está carregando vinte e quatro linhas, e a única coisa que muda entre os três botões é onde o decode acontece. Olhe o gráfico de frames, não a lista.
no orçamento perdido
Um frame que dura quase trezentos milissegundos. A lista não engasga — ela para, e o sistema operacional começa a considerar se o seu app ainda está vivo. Todo engenheiro mobile já subiu isso uma vez.
O que você acabou de aprender → O botão do meio é o capítulo inteiro. await suspende uma função, não move ela — a continuação retoma no main actor e o trabalho caro ainda cai dentro de um frame. Código assíncrono que nunca sai da main thread é o bug de performance mais comum em mobile, e é invisível até alguém abrir um profiler.
Ato um
A tela
Uma leitura não vale nada até alguém vê-la, e um celular dá 16,7 milissegundos para mostrá-la. Sete anos entregando app, nativo e cross-platform — e antes deles três anos um nível abaixo, em firmware, placas e peças usinadas, que a Apple Developer Academy transformou nisto: a mesma engenharia, movida para cima na pilha. Este ato são os últimos centímetros desse trajeto — chegar no tempo certo, continuar legível em condições que ninguém projetou, e alcançar só a pessoa certa.
02 · VIDRO Swift e Flutter · 2019 → hoje
O card que sobrevive ao mundo real
Qualquer um constrói o card do layout. O trabalho é construir o que continua legível quando a pessoa que segura o celular colocou a fonte no maior tamanho de acessibilidade, está em tela dividida, num idioma cujas palavras são mais longas, lendo da direita para a esquerda, numa conexão que nunca entregou a imagem. O designer te entrega um estado. Você entrega seis.
Um card, dois builds. À esquerda o card como ele sai do layout: uma linha só, cada texto numa linha, uma altura em pontos. À direita o mesmo card construído para sobreviver. Ligue as condições e veja qual continua legível — o tamanho de fonte que a pessoa realmente escolheu, uma tela dividida estreita, uma tradução mais longa, um idioma da direita para a esquerda, uma imagem que nunca chegou, um erro sem lugar para aparecer.
// The whole fix is three decisions.
HStack(alignment: .firstTextBaseline, spacing: 12) {
if let image { Thumbnail(image) } // no image, no box
VStack(alignment: .leading, spacing: 2) {
Text(position.symbol)
.font(.headline)
.lineLimit(nil) // let it wrap
if let error { Text(error).foregroundStyle(.red) }
else { Text(position.summary).font(.subheadline) }
}
Spacer(minLength: 12)
Text(position.pnl, format: .currency(code: code))
.monospacedDigit()
}
.padding(.horizontal, 16).padding(.vertical, 12)
.frame(minHeight: 44) // grows, never clips
.dynamicTypeSize(...DynamicTypeSize.accessibility5)
.accessibilityElement(children: .combine)
.accessibilityLabel(position.spokenLabel) // Same three decisions, same order.
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (image != null) Thumbnail(image!),
const SizedBox(width: 12),
Expanded( // min-width: 0, in Flutter
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(p.symbol, style: t.titleMedium, softWrap: true),
error != null
? Text(error!, style: t.bodySmall!.copyWith(color: cs.error))
: Text(p.summary, style: t.bodySmall),
],
),
),
const SizedBox(width: 12),
Text(p.pnl, style: t.titleMedium),
],
)
// Directionality mirrors the row; Semantics gives it one spoken label. O que você acabou de aprender → O layout desenhado no comp sobrevive a uma das sete condições aqui embaixo — justamente aquela para a qual ele foi desenhado. A diferença não é gosto, é qual pergunta o layout faz: não "a fonte está grande?" e sim "isso cabe?". Faça a segunda pergunta e uma tradução mais longa deixa de virar um chamado de bug.
03 · PORTA Tradx · 2022 → hoje
O login é por onde arrombam um app
Todo o resto num app mobile falha de forma visível. Isso falha em silêncio: o login funciona, a pessoa entra, e o fluxo ficou aberto o tempo inteiro. Eu refiz esse pedaço na Tradx depois de ler a nossa própria implementação com atenção — são as cinquenta linhas que eu perguntaria em qualquer entrevista sênior.
Autenticar alguém pelo navegador e devolver o resultado para o app são as cinquenta linhas mais sensíveis de um código mobile. Comece pela versão que todo tutorial mostra e feche uma chave por vez. A ordem importa mais do que você gostaria.
- O app abre o navegador ASWebAuthenticationSession, não uma web view — o cookie de sessão precisa ser o de verdade
- A pessoa faz login e o servidor de autorização redireciona de volta
- O aparelho roteia o redirect para qualquer app que reivindique aquele endereço
- O app lê o callback da URL, que vai para logs e histórico
- O app troca o código por uma sessão, sobre TLS, a partir do próprio app
- Alguém apresenta de novo o mesmo valor, uma segunda vez
- A sessão vai para o keychain e a pessoa entrou
- Sequestro de esquema qualquer outro app do aparelho pode registrar tradx:// e receber o redirect no seu lugar
- Token no log um access token na query string é gravado em logs do sistema, histórico do navegador e referrer
- Replay do código um código aceito duas vezes é um access token com passos a mais
- Interceptação do código sem PKCE a troca não prova nada sobre quem iniciou o fluxo, então quem tiver o código gasta ele
- Callback forjado sem um state impossível de adivinhar o app aceita um login que a pessoa nunca iniciou
Comece pela versão do tutorial e feche uma chave por vez. Veja até onde o redirect chega.
O que você acabou de aprender → Trocar o token por um código de uso único é a primeira correção que todo mundo tenta, e sozinha ela abre dois buracos para fechar um. Um código precisa de PKCE para que só o app que iniciou o fluxo o gaste, e de uso único para não ser gasto duas vezes. Meia migração de segurança é pior que nenhuma, porque parece terminada.
Ato dois
Com o que o app conversa
Volte a partir da tela e você encontra o trajeto que o número percorreu para chegar até ela. Ele precisa chegar enquanto ainda é verdade, ser encontrado entre centenas de milhares de irmãos, aguentar todo mundo perguntando ao mesmo tempo, e enfim valer uma decisão. Quatro capítulos — e no último, as quatro decisões são minhas.
04 · PULSO Tradx · 2022 → hoje
O cliente não devia precisar perguntar
Quase tudo que um app mostra é a resposta de uma pergunta que ele fez. Um preço ao vivo não é: quando a resposta chega, a pergunta já mudou, e perguntar de novo gasta uma requisição inteira para ouvir, quase sempre, que nada aconteceu. Este capítulo é a diferença entre um endpoint que responde e um canal que te avisa.
Um minuto de um feed de preços, reproduzido. Escolha como o cliente fica sabendo — perguntar num cronômetro, ou ser avisado — e então acompanhe os dois números: o que o mercado está fazendo, e o que o leitor está de fato olhando.
Uma conexão, {bytes} no minuto inteiro, e todos os {emitted} ticks chegaram. A tela mostrou um preço já superado em {behind} do tempo — o voo em si, e nada além disso.
O que você acabou de aprender → Perguntar uma vez por segundo custou oito vezes mais bytes do que manter um socket aberto — e ainda assim perdeu um quarto dos ticks. Frequência não é atualidade: o único jeito de parar de chegar atrasado é parar de perguntar.
05 · SERVIÇO Backend · 2018 → hoje
Alguém precisa lembrar de tudo isso
Uma leitura que ninguém guardou é uma leitura que não aconteceu. Atrás de cada dispositivo e de cada cliente existe uma API e um banco, e a diferença entre um produto rápido e um lento costuma ser uma única decisão tomada aqui. A tabela abaixo tem duzentas e quarenta mil leituras de sensor — do tipo que a sonda da outra página passou dois anos produzindo.
Uma tabela de leituras de sensor, 240.000 linhas. Escolha uma consulta, decida quais colunas ganham índice, e rode. A faixa abaixo é a tabela; repare em quanto dela o banco precisa tocar.
SELECT * FROM readings WHERE reading_id = 128374
índice · árvore B
18 pages
tabela · readings · 240.000 linhas
—
O que você acabou de aprender → Sem índice, o banco lê todas as linhas para te responder. Com um, ele salta direto ao ponto. Mesma query, mesmos dados, mesmo servidor — mil vezes mais rápido.
06 · ENXAME Backend · 2018 → hoje
Uma máquina nunca é suficiente
Um leitor é uma query; dez mil ao mesmo tempo é uma arquitetura. Tráfego não chega educadamente — ele chega todo de uma vez, às 3 da manhã, no dia do lançamento. Containers e Kubernetes são como você responde a isso sem acordar.
O tráfego deste serviço dispara a cada poucos segundos, como num dia de lançamento. Adicione réplicas e a latência cai; adicione demais e você paga por máquina ociosa. Depois entregue ao autoscaler e veja onde ele ainda perde.
prontos 0 subindo 0
Você está escalando na mão. O pico chega estando você pronto ou não.
O que você acabou de aprender → Você não deixou o código mais rápido — você fez mais cópias dele. Escala horizontal troca dinheiro por latência, e um autoscaler faz essa troca por você, sozinho.
07 · FLUXO Tradx · 2022 → hoje
E então eu construí a ferramenta
A Tradx é o meu sandbox — o lugar onde eu tomo todas as decisões e convivo com todas elas. Um cliente de trading para desktop escrito em Flutter, onde uma estratégia é um grafo que você liga em vez de um código que você escreve, publicado na Mac App Store e na Microsoft Store, com cerca de quinze mil downloads somando as duas. Somos dois construindo: eu e o Luiz Veloso, por pull request, com review que nenhum dos dois pula.
É isso que o Tradx faz, em miniatura. Você não escreve o laço — você liga a lógica, e o motor roda ela sobre os candles. Mexa nos parâmetros e veja o grafo e as operações mudarem juntos.
Duas pessoas, pull requests, e review que nenhum dos dois pula.
Ajuste até o número ficar bonito. Essa é a parte fácil, e é o que todo print de backtest na internet está te mostrando.
O que você acabou de aprender → Um backtest é uma hipótese, não um resultado. Qualquer um ajusta uma curva até ela ficar bonita nos dados que já tem; o ofício inteiro é saber quanto aquele número vale nos dados que ele não tem.
Ato três
E os modelos por baixo
Tudo até aqui move uma leitura que já existe. Este inventa a próxima — e é onde as duas metades deste site se revelam uma só. Eu treinei o modelo com dados de mercado; ele roda aqui sobre uma sonda de solo que eu enterrei anos antes, sem mudar uma linha, porque uma série temporal não sabe o que ela mede.
08 · MENTE StockGAN · 2021
Ensine a máquina a adivinhar o que vem depois
Em 2021 eu construí do zero uma rede adversária generativa para séries temporais — o trabalho em que a minha pós em IA e o mestrado em Bioengenharia deixam de ser linhas num diploma — duas redes, uma tentando inventar a próxima leitura e outra tentando pegá-la mentindo. Eu treinei em dados financeiros. Ela roda aqui numa sonda de solo que eu projetei e enterrei anos antes, sem mudar nada, porque uma série temporal não sabe o que ela mede.
Em 2021 eu escrevi uma GAN que prevê o próximo valor de uma série temporal — gerador, discriminador e loop de treino do zero. Eu treinei em trinta anos de barras diárias, porque era ali que havia histórico limpo de graça, mas a rede lê log-retornos e não faz ideia do que os números medem. Então aqui ela está rodando numa sonda de solo que eu construí anos antes de tudo isso. A parte que vale mostrar é esta: o discriminador nunca julga uma leitura prevista sozinha. A leitura é colada nas nove reais que vieram antes, e a rede precisa dizer se a janela inteira saiu do sensor. É esse o jogo. Você é o discriminador.
StockGAN · LSTM(128) · janela de 10 · treinada em 30 anos de barras diárias, rodando aqui no soil-1
Uma destas duas janelas termina com uma leitura que a sonda realmente fez. A outra termina com uma que o gerador inventou. Qual é qual?
Escolha uma. Nove leituras reais, e uma décima que é a sonda ou o meu gerador.
gerador LSTM(128, stateful) → Dropout(0.3) → Dense(n_features) discriminador LSTM(128, stateful) → Dropout(0.1) → Dense(1, sigmoid) RMSprop com decaimento polinomial de 2.5e-4 até 1e-4 ao longo de mil passos. Entropia cruzada com label smoothing em 0.2, para que o discriminador nunca tenha permissão de ter certeza absoluta de nada. Alvo real e alvo gerado multiplicados por ruído com σ = 0.25 durante o treino, para que ele não vença percebendo quais números são limpos demais. Doze indicadores — MACD, EMA, RSI, Bollinger, ROC, volatilidade — reduzidos por PCA e XGBoost antes de a rede ver qualquer coisa; numa sonda de solo esses mesmos doze viram médias móveis e taxa de variação da umidade, e é justamente esse o ponto: a arquitetura mudou de domínio e o pipeline de features quase não piscou. As funções de perda desta página são portes das reais, constantes incluídas; o gerador daqui é um substituto que reproduz o dilema sem carregar 128 unidades LSTM para dentro do seu navegador.
O que você acabou de aprender → Dois modelos, uma função de perda e um botão entre eles. Cobre do previsor que ele fique perto e ele responde com a média de tudo, que é segura e inútil. Cobre que ele seja crível e ele responde com algo que anda como o sensor e não sabe de nada. Todo modelo que eu entrego é essa troca feita de propósito, escrita e medida.
Ato quatro
E então saiu da tela
E agora a medida é o objeto. No Eldorado, em macOS e visionOS, uma coisa tridimensional deixa de ser algo que um engenheiro desenhou e passa a ser algo que uma câmera mediu — o mesmo movimento que eu fiz em 2019, repetido, agora sobre o mundo.
09 · CAMPO Eldorado · 2022 → hoje
E agora o objeto é feito de luz
O capítulo anterior inventou o próximo número de uma série. Este para de tratar o mundo como algo que alguém desenha. Uma cena deixa de ser uma superfície que alguém modelou e vira uma nuvem de gaussianas orientadas, ajustadas por gradiente descendente até que renderizações delas batam com as fotografias — o que significa que ninguém a desenha, e não sobra malha nenhuma para converter. Reconstruções de verdade começam com uma câmera andando por uma sala. A demonstração abaixo deliberadamente não: ela usa uma peça que eu mesmo projetei e imprimi, porque o único jeito de ver o que o método muda é assistir ao mesmo objeto construído dos dois jeitos.
Um objeto, construído duas vezes. De um lado da chave é a malha pela qual a peça foi impressa. Do outro é um campo de gaussianas sobre essa mesma malha — espalhadas aqui em vez de ajustadas, para os dois ficarem lado a lado e a diferença ser o método e mais nada.
carregando a malha…
997 triângulos, cada um colocado por alguém no SolidWorks em 2017.
O que você acabou de aprender → Uma malha diz onde estão as superfícies, e um engenheiro desenhou cada uma. Um campo de splats diz onde está a luz, e ninguém desenhou nada disso — foi ajustado até a renderização parar de discordar da fotografia.
10 · TRAJETÓRIA
As provas
Nove capítulos de demonstração. Este aqui é só evidência. Arraste pelos anos e veja o que estava rodando ao mesmo tempo — porque a manchete honesta desta linha do tempo não é nenhum emprego específico, é o quão raramente houve só um.
Formação
Pesquisa
Indústria
Empreendimentos
De trainee a sênior em quatro anos
Entrei no projeto Apple em 2022 como trainee e sigo no mesmo produto, hoje como sênior. Ninguém é promovido por calendário: cada degrau veio de pegar algo que ninguém queria — o processo de release, a suíte instável, a tela que todo mundo reescreveu duas vezes — e transformar aquilo num problema fácil para os outros. Arraste o ano e olhe a faixa de indústria: as quatro barras em sequência são o mesmo produto e o mesmo time, vistos de quatro níveis de responsabilidade diferentes.
Seis empresas
Fundadas ou cofundadas. Algumas cresceram, outras não — que é exatamente o motivo de fundar seis.
- Tradx Cofundador · fintech
- Agrom.IO Fundador · agritech
- Dommuz Cofundador · casa inteligente
- Psiu Cofundador · proptech
- Hubli Cofundador · edtech
- PreditChart Cofundador · fintech
Publicado e premiado
Seis trabalhos acadêmicos, três deles com revisão por pares, e nenhum sobre mobile — e é justamente esse o ponto. Foi ali que eu aprendi a errar em público: a descrever um método com precisão suficiente para outra pessoa atacá-lo, receber o parecer e mudar a afirmação. Esse hábito vale mais num code review do que qualquer um dos resultados.
- Instrumento para Medição de Fatores e/ou Elementos Naturais do Solo 2016 · Control & Automation Engineering, PUC-PR
- A Tool to Select FES Parameters for chronic SCI 2019 · 41st Annual International Conference of the IEEE EMBS · ler o artigo ↗ · doi:10.1109/EMBC.2019.8857421 · código ↗
- Indoors Wi-Fi Fall Detector Buckle for the Elderly 2019 · Advanced Materials Proceedings, 4(1), 40–45 · ler o artigo ↗ · doi:10.5185/amp.2019.1450 · código ↗
- Aplicação, in silico, de estímulos elétricos em modelo neuromuscular compatível com lesão medular crônica 2019 · M.Sc. dissertation in Bioengineering, PUC-PR · ler o artigo ↗ · código ↗
- Modelagem matemática do sistema de manutenção do equilíbrio por técnicas de identificação de sistemas 2018 · V Congresso Brasileiro de Eletromiografia e Cinesiologia · X Simpósio de Engenharia Biomédica · ler o artigo ↗
- Previsão de Séries Financeiras com Redes Generativas Adversárias 2020 · Applied AI specialization, PUC-PR
Na imprensa
- Hubli, criado na Apple Developer Academy — a Apple me nomeia entre os cinco criadores Apple Newsroom · 2021 · ler a matéria ↗
- Neon Wave, jogo de iOS que saiu no MacMagazine MacMagazine · 2020 · ler a matéria ↗
Eu construo o app, e construo aquilo com que ele conversa. A tela, o estado nela, a API que a preenche, o banco embaixo disso, e o modelo que lê tudo. A maioria das pessoas domina uma dessas. Eu já entreguei todas, e ainda sei qual é a parte difícil.
Com o que eu trabalho
Tudo aqui embaixo é creditado por algum capítulo desta página ou está num repositório público. Nada aqui é aspiracional.
- iOS · Swift
- Swift · SwiftUI · UIKit · visionOS · macOS · Swift Concurrency · async/await · actors · Combine · MVVM · Coordinators · Clean Architecture · Dependency injection · SwiftData · Core Data · URLSession · Swift Package Manager · XCTest · XCUITest · Instruments · Xcode · App Store Connect · TestFlight · VoiceOver · Dynamic Type · Localization · Push notifications · Deep linking
- Multiplataforma
- Flutter · Dart · Riverpod · Bloc · Provider · Kotlin · Jetpack Compose · Android · Platform channels · Offline-first · State management · Widget testing
- Publicação e release
- fastlane · Xcode Cloud · GitHub Actions · CI/CD · App Store review · Phased release · Firebase Crashlytics · Crash-free rate · Analytics · Feature flags · Semantic versioning
- Linguagens
- Swift · Dart · Kotlin · Java · TypeScript · JavaScript · Python · C · C++ · MATLAB · SQL
- Backend e APIs
- Node.js · NestJS · Express · Java · REST APIs · WebSockets · Microservices · OAuth 2.0 · JWT · API versioning · Distributed systems · System design · Event-driven architecture · API design
- Dados
- MongoDB · PostgreSQL · Redis · BullMQ · Database design · Indexing and query planning · Time series · Data pipelines
- Cloud e DevOps
- AWS · Amazon S3 · CloudFront · Route 53 · AWS IoT Core · IAM · EC2 · Docker · Kubernetes · Terraform · Infrastructure as Code · GitHub Actions · CI/CD · OIDC · Observability · Scalability · Linux
- Web
- Astro · HTML · CSS · Accessibility · Design systems · Responsive layout
- IA e 3D
- Machine learning · PyTorch · TensorFlow · Keras · GANs · LSTM · Deep learning · Feature selection · PCA · XGBoost · Gaussian Splatting · 3DGS · CUDA · Photogrammetry · Computer vision
- Embarcados e hardware
- Embedded C · ESP32 · PIC18F4550 · ATmega · PWM · ADC · MQTT · IoT · PCB design · EAGLE · Altium · SolidWorks · CAD · 3D printing · Design for manufacturing
- Jeito de trabalhar
- Git · Code review · Unit testing · Technical leadership · Mentoring · Product engineering · Backtesting · Paper trading
Fale comigo
Se alguma coisa aqui tem a cara de um problema que você tem, eu quero ouvir. E-mail ou LinkedIn, o que preferir — o CV é um PDF de duas páginas e o código está no GitHub.