Code review de código gerado por IA: 6 perguntas antes de aprovar o PR
Código de IA passa no lint e ainda erra o contexto. 6 perguntas para fazer code review de código gerado por IA antes de aprovar o PR.
O PR estava limpo. Lint verde, testes verdes, nomes bem escolhidos, até um comentário explicando a função. Você aprovou em dois minutos. Uma semana depois, descobriu que ele fazia a coisa errada com perfeição.
Esse é o problema central do code review de código gerado por IA: o código chega com cara de revisado. E a parte difícil quase nunca é a sintaxe. É o contexto que o agente não tinha e que só está na sua cabeça, no histórico do repositório e na conversa que rolou com o cliente.
Este post é uma opinião com método: seis perguntas, nessa ordem, que a gente usaria antes de aprovar um PR escrito por agente. No fim tem um checklist para copiar.
## Código "bonito" não é código certo para o seu projeto
Modelos são muito bons em produzir código plausível. Ele segue convenções gerais, usa as bibliotecas populares e compila. O que um modelo não sabe, a menos que alguém conte, é:
- o que o produto realmente precisa fazer naquele caso;
- por que o time decidiu não usar aquela biblioteca em 2023;
- quais clientes dependem do comportamento "estranho" que o PR acabou de "consertar".
Revisar só o diff avalia a qualidade do texto. Aprovar exige avaliar o encaixe. Por isso as perguntas abaixo começam fora do diff. (Para outra visão sobre o mesmo ponto, vale a leitura de AI Code Review in 2026: the hard part isn't the code, no dev.to: https://dev.to/rakbro/ai-code-review-in-2026-the-hard-part-isnt-the-code-457c)
## 1. Você leu os requisitos antes de abrir o diff?
Abra primeiro a issue, o ticket ou o prompt que originou a mudança. Escreva em uma frase o que deveria acontecer. Só então olhe o código.
Isso evita o viés de ancoragem: se você lê o diff primeiro, sua cabeça tenta justificar o que está na tela.
Exemplo hipotético: o requisito diz "aceitar apenas e-mails do domínio @empresa.com no cadastro de funcionários". O agente entrega uma validação de e-mail completa, com regex robusta, tratamento de maiúsculas e mensagem de erro amigável. Só que valida qualquer e-mail bem formado. O código é ótimo. O requisito não foi atendido.
O que o agente escreveu (exemplo hipotético): const isValid = EMAIL_REGEX.test(email);
O que o requisito pedia: const isValid = EMAIL_REGEX.test(email) && email.toLowerCase().endsWith("@empresa.com");
Takeaway: se você não consegue dizer o que o PR deveria fazer sem olhar o diff, ainda não está pronto para revisar.
## 2. Ele respeita os padrões e as decisões históricas do repositório?
Agentes tendem a resolver o problema do jeito mais comum na internet, não do jeito do seu projeto. Sinais de alerta:
- um helper novo que duplica um que já existe em lib/ ou utils/;
- uma segunda forma de fazer requisição HTTP, acesso a banco ou logging;
- uma dependência nova para algo que o projeto já resolve;
- uma abstração criada "por garantia", sem uso real.
Como checar: busque no repositório antes de aceitar. Um grep por nomes parecidos e uma olhada em como o módulo vizinho faz a mesma coisa resolvem a maior parte dos casos. Se existe uma decisão registrada (ADR, README, comentário), confira se o PR a contradiz.
Takeaway: o PR não deve só funcionar; deve parecer que foi escrito por quem conhece a casa.
## 3. Os testes provam algo?
Quando o mesmo agente escreve o código e o teste, os dois costumam compartilhar o mesmo erro de entendimento. O teste passa porque foi escrito para confirmar a implementação, não para questioná-la.
Desconfie especialmente quando:
- o teste repete a lógica do código (calcula o esperado com a mesma fórmula);
- só existem casos felizes;
- dados de teste foram ajustados para fazer a asserção passar;
- um teste existente foi alterado ou removido no mesmo PR.
No exemplo do e-mail: um teste que envia ana@empresa.com e espera sucesso passa tanto na versão certa quanto na errada. O teste que importa é "ana@gmail.com deve falhar", e ele não existe.
Uma checagem barata: quebre o código de propósito (inverta uma condição) e veja se algum teste falha. Se nada falha, o teste não protege nada.
Takeaway: leia os testes como leria a especificação. Se eles não descrevem o requisito, não servem de prova.
## 4. Segurança exige julgamento humano
Scanners pegam padrões conhecidos. Eles não sabem as regras de negócio de quem pode ver o quê.
Exemplo hipotético: um endpoint GET /invoices/:id que busca a fatura pelo id e devolve. Autenticação ok, consulta parametrizada, sem SQL injection. Mas não checa se a fatura pertence ao tenant do usuário logado. Em um sistema multi-tenant, isso é vazamento de dados entre clientes.
Passa em qualquer SAST comum (exemplo hipotético): const invoice = await db.invoice.findUnique({ where: { id } });
O que o contexto exige: const invoice = await db.invoice.findFirst({ where: { id, tenantId: user.tenantId } });
Perguntas para fazer em todo PR que toca dados: quem pode chamar isso? De quem são os dados que voltam? O que acontece se eu trocar o id na URL?
Takeaway: autorização é regra de negócio. Nenhuma ferramenta sabe a sua; você sabe.
## 5. Qual é o raio de impacto?
Um diff pequeno pode ter consequência grande, e um diff grande pode ser inofensivo. O que importa é o que depende do que mudou.
Antes de aprovar, responda:
- Quem chama essa função, rota ou tabela? (busque os usos, não confie no resumo do agente.)
- Há migração de banco? Ela é reversível? Roda bem com dados reais em volume?
- Muda contrato de API, evento, formato de arquivo ou variável de ambiente?
- Dá para fazer rollback rápido se der errado? Existe feature flag?
Revise com mais cuidado o que é difícil de desfazer (dados, contratos públicos, cobrança, permissões) e com menos o que é fácil de reverter (texto de UI, estilo).
Takeaway: o esforço de revisão deve ser proporcional ao custo de errar, não ao número de linhas.
## 6. O que dá para automatizar já está automatizado?
Se um humano está apontando manualmente erro de formatação, import não usado ou padrão inseguro conhecido, o time está gastando atenção no lugar errado. Deixe para máquinas:
- formatação e lint;
- checagem de tipos;
- análise estática e SAST para padrões conhecidos;
- scanner de segredos;
- testes e build como CI obrigatório, bloqueando o merge.
Isso libera o humano para o que só o humano faz: requisitos, encaixe, intenção dos testes, regras de autorização e impacto. A divisão é simples: máquina verifica regras, pessoa verifica contexto.
## Checklist copiável
- Li a issue/requisito antes do diff e resumi o objetivo em 1 frase
- O comportamento implementado bate com o requisito, inclusive nos casos de borda
- Não duplica helpers, padrões ou dependências que o repositório já tem
- Respeita decisões registradas (ADR, README, convenções do módulo)
- Os testes cobrem o requisito, não só a implementação (há caso que deveria falhar)
- Nenhum teste existente foi removido ou enfraquecido sem justificativa
- Autorização e limites multi-tenant verificados (quem acessa, de quem são os dados)
- Mapeei quem depende do que mudou (usos, contratos, migrações)
- Há plano de rollback para o que é difícil de desfazer
- CI verde: lint, tipos, SAST, scanner de segredos e testes
## Fechando
O agente escreve rápido, e isso desloca o gargalo: o trabalho difícil deixa de ser produzir código e passa a ser decidir se aquele código é o certo. Ler requisitos antes do diff, checar o encaixe, desconfiar de teste escrito junto, tratar segurança como regra de negócio e medir o raio de impacto resolve a maior parte do risco. O resto, automatize.
Agora a pergunta para você: qual dessas seis teria pegado o último PR ruim que passou pela sua revisão? Conta nos comentários. Se for uma que não está na lista, melhor ainda.