ConstructiCat Logo
CodeBust.
Browse section ▾

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.

##Signs and Symptoms

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 undefinedCannot 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 async sem try/catch e 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(...)!, casts as ou asserções de não-nulo (!) no lugar de um tratamento real.
  • // TODO: handle errors ou um catch (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.

##Reasons for the Problem

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 wrapper Result<T>, um esquema zod ou 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 Evitação de Refatorações e Fixação no Manual entre seus principais antipadrões de IA — o modelo produz código funcional para o prompt imediato e nunca o robustece. O arXiv 2509.20491 cataloga smells específicos de IA em torno de falhas silenciosas; o arXiv 2510.03029 encontra smells de implementação elevados, como Bloco Catch Vazio, na saída de LLMs.

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 descreve isso como código "inseguro por burrice," entregue rápido e sem critério.
  • 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 — linhas copiadas/coladas subiram de 8,3% (2021) para 12,3% (2024), blocos duplicados de 5+ linhas cresceram ~8x em 2024, enquanto a refatoração (linhas movidas) caiu de ~25% para menos de 10%. O tratamento de erros ausente é remendado depois, duplicado em cada ponto de chamada, nunca centralizado.

##Treatment

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/Result existente de lib/errors.ts e o logger de lib/log.ts; valide a resposta com o esquema zod em schemas/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 eslint e tsc --noEmit e corrija cada aviso, incluindo @typescript-eslint/no-floating-promises." A verificação de tipos com strictNullChecks transforma 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: Introduzir Cláusula de Guarda (throw antecipado em estado inválido), Introduzir Asserção / validação de fronteira (parse-don't-validate nas bordas de IO) e Introduzir Caso Especial / Objeto Nulo (?? "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, faça Extrair Função dele em 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).

##Detected by

  • typescript-eslint @typescript-eslint/no-floating-promisesSinaliza promises cujo caminho de rejeição nunca é tratado (sem await/catch) — um sintoma comum de caminho feliz em chamadas assíncronas.
  • ESLint no-emptySinaliza blocos vazios, incluindo blocos catch vazios — o 'tratamento de erros' de fachada que o modelo deixa para trás.
  • SonarSource (JS/TS) javascript:S2486Exceções não devem ser ignoradas — sinaliza erros capturados mas engolidos, o primo degenerado da ausência total de tratamento.