💡 Key Takeaways
- The First 30 Seconds: What the PR Description Tells Me
- The Logic Layer: Does This Actually Solve the Problem?
- The Data Layer: Following the Information Flow
- The Security Lens: Thinking Like an Attacker
Três anos atrás, aprovei uma solicitação de pull que custou à minha empresa $47.000 em receita perdida em um único fim de semana. O código parecia bom. Os testes passaram. A lógica era sólida. Mas eu perdi algo sutil em como tratamos as conversões de fuso horário para nossos clientes europeus, e quando o horário de verão começou, nosso sistema de processamento de pagamentos começou a falhar silenciosamente para 18% da nossa base de usuários.
💡 Principais Aprendizados
- Os Primeiros 30 Segundos: O que a Descrição da PR Me Diz
- A Camada Lógica: Isso Realmente Resolve o Problema?
- A Camada de Dados: Seguindo o Fluxo de Informação
- A Lente de Segurança: Pensando Como um Atacante
Esse incidente mudou para sempre como eu reviso o código. Sou Sarah Chen, e fui gerente de engenharia sênior em três empresas de SaaS diferentes na última década, revisando uma média de 15 a 20 solicitações de pull por semana. Isso dá aproximadamente 8.000 PRs na minha carreira, que vão desde correções de bugs em uma linha até enormes refatorações arquitetônicas abrangendo mais de 50 arquivos. Eu já vi códigos brilhantes que enviaram bugs, e códigos confusos que funcionaram perfeitamente em produção durante anos.
O que eu aprendi é que a revisão de código não se trata realmente de encontrar bugs — embora isso seja importante. Trata-se de construir sistemas que sobrevivam ao contato com a realidade. Trata-se de criar código que o próximo desenvolvedor (frequentemente você mesmo, seis meses depois) possa entender e modificar sem medo. E trata-se de capturar os problemas invisíveis que só se revelam quando sua aplicação escala, quando casos extremos surgem ou quando aquele cliente na Austrália faz algo que você nunca antecipou.
Esta é a minha lista de verificação. Não a teórica dos livros didáticos, mas a testada em batalha que refinei através de inúmeros incidentes em produção, sessões de depuração noturnas e aquela sensação de afundamento quando você percebe que um bug que aprovou agora está afetando usuários reais.
Os Primeiros 30 Segundos: O que a Descrição da PR Me Diz
Antes de olhar para uma única linha de código, eu gasto 30 segundos lendo a descrição da PR. Isso pode parecer trivial, mas uma descrição mal escrita é frequentemente o primeiro sinal de alerta de que o código em si pode ter problemas. Na minha experiência, desenvolvedores que não conseguem articular claramente o que seu código faz frequentemente não pensaram completamente na implementação.
Estou procurando três coisas específicas: o problema sendo resolvido, a abordagem tomada e quaisquer compensações feitas. Uma boa descrição de PR lê como um mini documento de design. "Correção de bug na autenticação do usuário" não me diz nada. "Corrigido condição de corrida na atualização do token JWT que causou o logout inesperado de 0,3% dos usuários durante períodos de alta demanda, implementando um bloqueio distribuído usando Redis" me diz que o desenvolvedor entende profundamente o problema.
Eu também verifico se a PR está com o tamanho apropriado. Minha regra prática: se uma PR toca mais de 400 linhas de código (excluindo testes e arquivos gerados), provavelmente é grande demais para revisar de maneira eficaz. Pesquisas das práticas de engenharia do Google sugerem que a eficácia da revisão cai drasticamente após 200-400 linhas. Descobri que isso é preciso — após revisar cerca de 300 linhas, minha atenção começa a vacilar, e começo a perder problemas sutis.
Quando encontro uma PR massiva, peço ao desenvolvedor para dividi-la em blocos menores e lógicos. Sim, isso leva mais tempo inicialmente, mas evita o cenário onde eu aprovo uma PR de 2.000 linhas porque estou sobrecarregada e só quero passar por ela. Aprovai muitas PRs problemáticas desse jeito no início da minha carreira.
A descrição também deve vincular a contexto relevante: o ticket ou problema sendo abordado, quaisquer documentos de design e PRs relacionadas. Se eu tiver que procurar no Jira ou no Slack para entender por que essa mudança está sendo feita, isso cria fricção que desacelera todo o processo de revisão. Na minha empresa atual, estabelecemos a política de que PRs sem contexto adequado não são revisadas até que a descrição seja atualizada. Essa simples regra melhorou nossa qualidade de revisão de maneira mensurável.
A Camada Lógica: Isso Realmente Resolve o Problema?
Uma vez que eu entenda o que a PR supõe fazer, eu verifico se ela realmente faz. Isso soa óbvio, mas você ficaria surpreso com a frequência com que o código passa em testes e ainda assim não aborda completamente o problema subjacente. Já vi desenvolvedores corrigirem o sintoma enquanto deixam a causa raiz intocada, criando uma bomba-relógio que irá explodir mais tarde.
"A revisão de código não se trata realmente de encontrar bugs — trata-se de construir sistemas que sobrevivem ao contato com a realidade e de criar código que o próximo desenvolvedor pode entender sem medo."
Começo mentalmente percorrendo o caminho feliz. Se esta é uma nova funcionalidade para processar reembolsos, imagino um cliente solicitando um reembolso e rastreio o caminho do código desde o endpoint da API passando pela lógica de negócios até a atualização do banco de dados. Cada passo faz sentido? Estamos lidando com os dados corretamente? Estamos atualizando todos os registros necessários?
Mas o caminho feliz é fácil. O que separa um bom código de um código pronto para produção é como ele lida com os caminhos infelizes. O que acontece se o gateway de pagamento estiver fora do ar? E se o valor do reembolso for negativo? E se o usuário solicitar um reembolso por um pedido que já foi reembolsado? Aprendi a ser quase paranoica com casos extremos porque ambientes de produção são motores de caos que encontrarão cada caso extremo que você não considerou.
Eu procuro padrões de programação defensiva: verificações de nulo, validação de entrada, tratamento de condições de limite. Mas também procuro por super Engenharia. Nem toda função precisa lidar com cada possível caso extremo. Se um método privado é chamado de apenas um lugar com entrada validada, adicionar validação redundante é apenas ruído. A chave é entender o contrato de cada função e garantir que ele seja mantido nas bordas.
Um padrão que vi causar problemas repetidamente é o "atualização otimista." Um desenvolvedor assume que uma operação terá sucesso e atualiza o estado da interface do usuário ou do banco de dados antes de confirmar o sucesso. Isso funciona 99% das vezes, mas aquele 1% cria bugs incrivelmente confusos onde o estado do sistema se torna inconsistente. Sempre sinalizo esses padrões e pergunto: o que acontece se essa operação falhar no meio do caminho?
A Camada de Dados: Seguindo o Fluxo de Informação
Erros de dados são o pior tipo de erro porque costumam ser silenciosos e cumulativos. Um erro de lógica pode travar sua aplicação imediatamente, mas um erro de dados pode corromper seu banco de dados lentamente ao longo de semanas, e quando você perceber, terá milhares de registros ruins e nenhuma forma limpa de corrigi-los.
| Foco da Revisão | Revisor Júnior | Revisor Sênior | Impacto na Produção |
|---|---|---|---|
| Sintaxe & Estilo | Foco primário | Atenção automatizada/mínima | Baixo |
| Casos Extremos | Apenas cenários óbvios | Questões de fuso horário, localidade, escala | Alto |
| Cobertura de Testes | Testes existem e passam | Testes cobrem modos de falha | Crítico |
| Desempenho | Código funciona corretamente | Comportamento sob carga/escala | Alto |
| Documentação | Comentários presentes | Por que as decisões foram tomadas | Médio |
Quando reviso código que toca em dados, traço todo o ciclo de vida desses dados. De onde eles vêm? Como são validados? Como são transformados? Onde são armazenados? Quem pode acessá-los? Isso é especialmente crítico para conteúdo gerado pelo usuário ou dados financeiros onde erros têm consequências reais.
Presto atenção especial às conversões de tipo de dados. Uma vez, aprovei um código que convertia um preço de um float para um inteiro para um cálculo, e depois de volta para um float. Parecia inofensivo. Mas devido a problemas de precisão de ponto flutuante, preços como $10.99 ocasionalmente se tornavam $10.98 ou $11.00 após a ida e volta. Não percebemos até que os clientes começaram a reclamar sobre cobranças incorretas. Agora, analiso cada conversão de tipo, especialmente envolvendo dinheiro ou medidas.
Migrações de banco de dados são outra área onde sou extra cautelosa. Estou atenta a várias coisas: A migração é reversível? O que acontece se falhar no meio do caminho? Quanto tempo levará em nosso banco de dados de produção? Já vi migrações que funcionaram bem em um banco de dados de desenvolvimento com 1.000 registros, mas travaram tabelas por 20 minutos em um banco de dados de produção com 50 milhões de registros, derrubando toda a aplicação.
Para qualquer código que modifica dados existentes, pergunto: há um plano de backup? Podemos reverter isso se algo der errado? Na minha empresa anterior, tínhamos uma política de que qualquer migração de dados que afetasse mais de 10.000 registros exigia uma simulação em um snapshot de produção e um procedimento explícito de reversão documentado na PR. Isso nos salvou várias vezes.
Também olho para padrões de acesso a dados. Este código está fazendo consultas N+1? Está carregando coleções inteiras na memória quando só precisa de alguns campos? Esses problemas de desempenho podem não ser óbvios no desenvolvimento, mas se tornam críticos em escala. Uma vez, revisei um código que parecia bom, mas teria feito a CPU do nosso banco de dados subir a 100% se tivesse sido enviado para produção, simplesmente porque era