As seis formas do código gerado quebrar em produção
Seis padrões concretos, com o código real de cada um: injeção, N+1, erro engolido, corrida, regra inventada e dependência supérflua.
A aula anterior tratou do erro que aparece na primeira execução. Esta trata dos outros — os que passam no seu teste manual, entram no merge e aparecem três semanas depois.
- Reconhecer os seis padrões de falha mais frequentes em código assistido.
- Saber qual verificação específica pega cada um deles.
- Transformar isso em critério de revisão, não em desconfiança genérica.
| # | Padrão | O que pega |
|---|---|---|
| 1 | SQL montado por concatenação | Análise estática + revisão de toda query |
| 2 | Consulta dentro de laço (N+1) | Log de queries com contador |
| 3 | Erro capturado e engolido | Busca por catch sem log ou rethrow |
| 4 | Verificar-depois-agir sob concorrência | Teste de carga ou revisão de transação |
| 5 | Regra de negócio inventada | Confronto com o requisito — só humano pega |
| 6 | Dependência nova sem necessidade | Revisão do diff do arquivo de pacotes |
1. SQL montado por concatenação
Peça "uma busca por nome com filtro opcional de status" e é comum receber a montagem dinâmica da query com interpolação — especialmente quando o prompt menciona que o filtro é opcional, porque a saída mais provável para "opcional" é concatenar condições.
$sql = "SELECT * FROM users WHERE name LIKE '%$nome%'";
if ($status) {
$sql .= " AND status = '$status'";
}
$rows = $pdo->query($sql)->fetchAll();Um nome com aspa simples derruba a query. Um nome bem escolhido lê a tabela inteira.
$sql = 'SELECT * FROM users WHERE name LIKE :nome';
$params = ['nome' => '%' . $nome . '%'];
if ($status !== '') {
$sql .= ' AND status = :status';
$params['status'] = $status;
}
$stmt = $pdo->prepare($sql);
$stmt->execute($params);A estrutura da query continua dinâmica; o valor nunca entra por concatenação.
A diferença: O que muda não é o SQL ser fixo — é o dado do usuário nunca virar parte do texto da query. Nomes de coluna e direção de ordenação, quando precisarem ser dinâmicos, saem de uma lista fechada no seu código, nunca do request.
2. Consulta dentro de laço
$pedidos = $pedidoRepo->doMes($mes);
foreach ($pedidos as $pedido) {
$pedido['cliente'] = $clienteRepo->find($pedido['cliente_id']); // 1 query por pedido
$pedido['itens'] = $itemRepo->doPedido($pedido['id']); // mais 1 por pedido
}Esse é o padrão mais traiçoeiro da lista porque o código está correto. Ele produz o resultado certo. Só não sobrevive ao volume real. E o assistente não conhece o seu volume real — a menos que você diga.
Informe a ordem de grandeza: "essa listagem roda com até 10 mil pedidos, cada um com 3 a 40 itens". A resposta muda de laço para *join* ou carregamento em lote quase sempre.
3. Erro capturado e engolido
try {
await enviarNotaFiscal(pedido);
} catch (e) {
// segue o fluxo
}
return { ok: true };ok: true mesmo quando a nota não foi emitida. O incidente vira "o sistema diz que enviou".Modelos produzem esse padrão porque ele aparece muito em código de exemplo, onde o objetivo é não poluir a demonstração. Em produção, o catch vazio é a diferença entre um erro de dez minutos e uma investigação de dois dias.
A correção é sempre a mesma: registre com contexto, decida explicitamente se o fluxo continua, e faça o retorno dizer a verdade.
4. Verificar-depois-agir sob concorrência
// Gerado a partir de "não deixar reservar se não houver vaga"
$vagas = $repo->vagasDisponiveis($eventoId);
if ($vagas > 0) {
$repo->criarReserva($eventoId, $usuarioId); // ← duas threads chegam aqui juntas
$repo->decrementarVagas($eventoId);
}if está certo; a suposição de que nada acontece entre a leitura e a escrita é que está errada.Concorrência é o assunto em que a resposta padrão do assistente é mais fraca, porque o código correto depende do seu banco, do seu nível de isolamento e de onde o processo roda. Aqui o prompt precisa ser explícito: "duas requisições podem chegar ao mesmo tempo; resolva no banco, com transação e trava, não em memória".
5. Regra de negócio inventada
Quando o requisito não diz o que fazer num caso, o modelo não pergunta — ele preenche. Peça "calcular o valor do frete grátis acima de R$ 200" e alguma resposta vai decidir sozinha se o valor considerado é antes ou depois do desconto, se inclui itens de terceiros, e o que acontece num cancelamento parcial.
Este é o único item da lista que nenhuma ferramenta pega. Não existe linter para regra errada. Só o confronto com o requisito e com quem o escreveu. É o assunto do módulo 2.
6. Dependência nova sem necessidade
Peça uma formatação de data e você pode receber uma biblioteca inteira junto. Nem sempre é errado — mas a decisão de adicionar uma dependência tem custo de segurança, build e manutenção, e ela chega escondida no meio de um diff que parecia ser de uma função.
Leia sempre o diff do package.json, composer.json ou requirements.txt. É a linha do diff que menos recebe atenção e a que mais muda o projeto.
- Nenhum valor vindo do usuário foi concatenado em query.
- Nenhuma consulta ao banco ou chamada de rede ficou dentro de laço.
- Todo
catchregistra o erro ou o propaga — nenhum está vazio. - Onde há concorrência real, a garantia está no banco e não num
if. - Cada regra de negócio do código foi confrontada com o requisito escrito.
- O diff do arquivo de dependências foi lido linha a linha.
- Cinco dos seis padrões têm uma ferramenta que os pega. O sexto — regra inventada — só uma pessoa pega, e é por isso que revisar continua sendo trabalho seu.
Comentários e dúvidas
Inscreva-se grátis para comentar, tirar dúvidas, marcar seu progresso e emitir o certificado ao fim do curso.
Inscrever-se grátis com GoogleAinda não há comentários nesta aula.