← Curso IA na prática para Programadores: do prompt ao pull request 22 aulas
Como a IA erra (e como isso vira bug no seu commit)

As seis formas do código gerado quebrar em produção

Armadilha 9 min de leitura

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.

Ao final desta aula você consegue
  • 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ãoO que pega
1SQL montado por concatenaçãoAnálise estática + revisão de toda query
2Consulta dentro de laço (N+1)Log de queries com contador
3Erro capturado e engolidoBusca por catch sem log ou rethrow
4Verificar-depois-agir sob concorrênciaTeste de carga ou revisão de transação
5Regra de negócio inventadaConfronto com o requisito — só humano pega
6Dependência nova sem necessidadeRevisã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.

O que costuma vir
$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.

O que você troca
$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

Funciona perfeitamente com os 12 registros do seu ambiente localphp
$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
}
Com 12 pedidos: 25 queries, 40 ms, ninguém nota. Com os 8.000 pedidos de produção: 16.001 queries e um timeout.

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.

O que dizer no prompt

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

O padrão que apaga a evidênciajavascript
try {
  await enviarNotaFiscal(pedido);
} catch (e) {
  // segue o fluxo
}

return { ok: true };
A função responde 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

Duas requisições simultâneas passam pelo mesmo ifphp
// 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);
}
Com a última vaga e dois cliques simultâneos, saem duas reservas. O 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.

Checagem antes de seguir
  • Nenhum valor vindo do usuário foi concatenado em query.
  • Nenhuma consulta ao banco ou chamada de rede ficou dentro de laço.
  • Todo catch registra 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.
Em uma frase
  • 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 Google

Ainda não há comentários nesta aula.