Código Apenas do Caminho Feliz.
Assistentes de IA tendem a gerar código que trata apenas o caso bem-sucedido e bem-formado — pulando validação de entrada, tratamento de erros, verificações de nulo/vazio e casos extremos — de modo que o código funciona na demonstração e quebra em produção.
##Sinais e sintomas
Um revisor identifica código apenas do caminho feliz quando cada linha assume que a anterior teve sucesso: chamadas de rede não são verificadas quanto a status diferente de 2xx, JSON.parse/await res.json() nunca é encapsulado, campos anuláveis são desreferenciados diretamente, arrays são indexados sem verificar o tamanho e a entrada externa flui direto para a lógica sem nenhuma validação. Em geral há exatamente um caminho de retorno e nenhum throw, nenhuma cláusula de guarda e nenhum catch (ou um catch que está vazio ou apenas com console.log).
// Gerado por IA: apenas o cenário de sucesso existe
async function getUserCity(userId) {
const res = await fetch(`/api/users/${userId}`);
const user = await res.json();
return user.address.city.toUpperCase();
}
Modos de falha silenciosamente ausentes: res.ok nunca é verificado (404/500 retorna um corpo de erro que .json() pode rejeitar), user poderia ser {}, address poderia ser null, city poderia ser undefined → Cannot read properties of undefined. A função "funciona" contra um stub feliz e lança erro contra a realidade.
Sinais reveladores em um diff:
- Uma nova função
asyncsemtry/catche sem.catch()na promise. - Cadeias de propriedades diretas (
a.b.c.d) sobre dados que cruzaram uma fronteira de confiança/IO. arr[0]sem verificação,find(...)!, castsasou asserções de não-nulo (!) no lugar de um tratamento real.// TODO: handle errorsou umcatch (e) {}vazio deixado como espaço reservado.- A descrição do PR diz "trata X", mas apenas o ramo em que X dá certo está implementado.
##Causas do problema
Por que os modelos produzem isso
- A probabilidade do próximo token favorece o fluxo canônico. A continuação mais provável depois de
const user = await res.json()éreturn user.something— não uma verificação de status. O tratamento de erros é um boilerplate de alta variância que muda de uma base de código para outra, então é estatisticamente "surpreendente" e acaba descartado. - Os dados de treinamento são enviesados para o caminho feliz. Tutoriais, trechos de README, posts de blog e respostas aceitas no Stack Overflow removem validação e tratamento de erros por brevidade ("tratamento de erros omitido por clareza"). O modelo aprendeu com textos que deliberadamente removeram justamente o código que você quer.
- Bajulação / recompensa por parecer limpo. Assistentes ajustados por RLHF são recompensados por respostas concisas e diretamente responsivas. Código defensivo parece ruído, então o modelo otimiza para o trecho arrumado que aparenta responder ao prompt.
- Sem contexto do repositório. O modelo não sabe que você tem um tipo
AppError, um wrapperResult<T>, um esquemazodou uma convenção de logging, então não consegue reutilizá-los — e recorre por padrão a "nada" em vez de adivinhar suas convenções. - Tendências comportamentais documentadas. A análise da OX Security de mais de 300 repositórios cita a Evitação de Refatorações — uma IA que "se destaca gerando código novo, mas nunca volta para melhorar a arquitetura" — e a Fixação no Manual entre seus principais antipadrões de IA (relatório OX): o modelo produz código funcional para o prompt imediato e não volta para torná-lo robusto. Além disso, código Java gerado por LLMs apresentou em média 73% mais smells de implementação do que as soluções humanas de referência, com os maiores aumentos em incompletude (Incompleteness), nomenclatura inconsistente (Inconsistent Naming Convention) e redundância (Redundancy) (arXiv 2510.03029).
Por que isso é prejudicial
- Correção: quebra com
null, coleções vazias, timeouts e respostas diferentes de 200 — exatamente as entradas que não aparecem em um teste manual rápido. - Segurança: o caminho feliz confia implicitamente em sua entrada. Validação ignorada nas fronteiras é a porta por onde entram bugs de injeção, path traversal e prototype pollution. O relatório da OX usa a expressão "insecure by dumbness" (inseguro por burrice) para apps que usuários não técnicos constroem com IA e implantam sem conhecimento de segurança — exatamente onde caminhos felizes sem validação vão para produção sem revisão (OX Security).
- Carga de revisão e depuração: na Stack Overflow Developer Survey de 2025, 66% dos desenvolvedores citam "soluções de IA que estão quase certas, mas não totalmente" como sua maior frustração e 45% dizem que depurar código gerado por IA consome mais tempo. O código de caminho feliz é o arquétipo do "quase certo" — lê-se bem e falha em tempo de execução.
- Acúmulo de dívida técnica: como o modelo não refatora nem reutiliza utilitários de erro existentes, cada função de caminho feliz é um novo bloco de uso único. Os dados de 2025 da GitClear mostram o padrão mais amplo (GitClear 2025: linhas movidas/refatoradas 25% (2021) → menos de 10% (2024); copy/paste 8,3% → 12,3%; blocos duplicados de 5+ linhas ~8× em 2024). O tratamento de erros ausente é remendado depois, duplicado em cada ponto de chamada, nunca centralizado.
##Tratamento
Táticas de revisão e de prompting
- Faça o modelo enumerar os modos de falha primeiro. Prompt: "Antes de escrever código, liste os modos de falha desta função (entrada inválida, erro de rede, status diferente de 200, resposta vazia/malformada, campos ausentes, concorrência). Depois implemente o tratamento de cada um." Forçar a etapa de enumeração contraria a tendência do próximo token de ignorá-los.
- Aponte as convenções a reutilizar. "Use nosso tipo
AppError/Resultexistente delib/errors.tse ologgerdelib/log.ts; valide a resposta com o esquemazodemschemas/user.ts." Isso converte "sem tratamento" em reutilização em vez de um bloco sob medida (evita Código Duplicado). - Exija que os portões sejam executados. "Rode
eslintetsc --noEmite corrija cada aviso, incluindo@typescript-eslint/no-floating-promises." A verificação de tipos comstrictNullCheckstransforma desreferências silenciosas de nulo em erros de compilação que o modelo precisa resolver. - Exija os testes negativos. Peça testes unitários cobrindo as entradas vazias/nulas/de erro, não apenas o caso feliz — a ausência desses testes é, por si só, o smell.
A refatoração
Adicione validação de fronteira e cláusulas de guarda, e centralize o tratamento. Movimentos nomeados: cláusulas de guarda como em Substituir Condicional Aninhada por Cláusulas de Guarda (throw antecipado em estado inválido), Introduzir Asserção junto com validação de fronteira (parse-don't-validate nas bordas de IO) e Introduzir Objeto Nulo (um valor de caso especial como ?? "UNKNOWN" em vez de quebrar).
// depois: os modos de falha são cidadãos de primeira classe
async function getUserCity(userId: string): Promise<string> {
if (!userId) throw new InvalidArgumentError("userId required"); // cláusula de guarda
const res = await fetch(`/api/users/${encodeURIComponent(userId)}`);
if (!res.ok) throw new ApiError(`user fetch failed: ${res.status}`);
const user = UserSchema.parse(await res.json()); // validar na fronteira
return user.address?.city?.toUpperCase() ?? "UNKNOWN"; // trata a lacuna como caso especial
}
Se o mesmo padrão de try/validar/log começar a se repetir em vários pontos de chamada, aplique Extrair Método para movê-lo para um helper compartilhado fetchJson<T>(url, schema) para que o tratamento de erros viva em um só lugar em vez de ser copiado e colado (a armadilha de duplicação da GitClear).
##Detectado por
- typescript-eslint @typescript-eslint/no-floating-promises — Sinaliza promises cujo caminho de rejeição nunca é tratado (sem await/catch) — um sintoma comum de caminho feliz em chamadas assíncronas.
- ESLint no-empty — Sinaliza blocos vazios, incluindo blocos catch vazios — o 'tratamento de erros' de fachada que o modelo deixa para trás.
- SonarSource (JS/TS) javascript:S2486 — Exceções não devem ser ignoradas — sinaliza erros capturados mas engolidos, o primo degenerado da ausência total de tratamento.