ConstructiCat Logo
CodeBust.
Browse section ▾

Configuração Hardcoded.

Assistentes de IA incorporam valores específicos de ambiente — URLs, portas, caminhos, timeouts, chaves e números mágicos — diretamente na lógica, em vez de lê-los a partir de configuração ou do ambiente, porque um literal é o próximo token mais provável e o modelo desconhece sua camada de configuração existente.

##Signs and Symptoms

Um assistente de IA tende a emitir um literal plausível exatamente onde deveria haver uma referência. O código roda na demonstração, então o smell sobrevive à revisão a menos que você o procure.

Sinais reveladores:

  • Endpoints, portas, caminhos e timeouts inline espalhados pela lógica de negócio em vez de um módulo de configuração: http://localhost:3000, /tmp/cache, 5432, setTimeout(..., 30000).
  • Números/strings mágicos sem uma constante nomeada — contagens de retentativa, tamanhos de página, limites de taxa, limiares de funcionalidades.
  • O mesmo literal repetido em vários arquivos porque o modelo o regenerou em vez de importar uma constante existente (o padrão "copia/cola para cima, refatora para baixo" da GitClear em miniatura).
  • Credenciais / chaves hardcoded — tokens de API, chaves de criptografia ou strings de autenticação Basic coladas dos dados de treinamento.
  • Um config.ts / .env / objeto de configurações já existente que o novo código ignora por completo.
  • Valores fixados por ambiente: uma URL de dev/staging ou ID de conta de teste fixado em um caminho que vai para produção.
// 🚩 Gerado por IA: cada parâmetro é um literal, valores de dev embutidos
export async function syncOrders() {
  const res = await fetch("https://api.staging.acme.dev/v1/orders", {
    headers: { Authorization: "Bearer sk_test_4eC39HqLyjWDarjtT1zdp7dc" },
    signal: AbortSignal.timeout(30000),
  });
  const orders = (await res.json()).slice(0, 50); // por que 50?
  for (let i = 0; i < 3; i++) { /* tenta novamente 3x... por que 3? */ }
}

A linha Authorization é um alerta do gitleaks/Semgrep; a URL, 30000, 50 e 3 são smells de valores mágicos; e api.staging.acme.dev é um valor de dev prestes a chegar à produção.

##Reasons for the Problem

Por que os modelos produzem isso

  • Um literal é o token seguinte de maior probabilidade. Dado fetch(, a conclusão mais barata que satisfaz o prompt imediato é uma string de URL concreta, não config.apiBaseUrl. Resolver uma referência exige saber que um símbolo existe em outro lugar; emitir um valor não exige nada. Os modelos otimizam para um trecho localmente plausível e executável.
  • O corpus de treinamento é composto de trechos autocontidos. Tutoriais, exemplos de README e respostas do Stack Overflow embutem seus valores para que rodem de forma independente. O modelo aprendeu que "bom código de exemplo" usa hardcode — o oposto da higiene de produção.
  • Sem contexto do repositório / janela limitada. Normalmente o modelo não leu seu config/, o schema de env ou o arquivo de constantes, então não consegue reutilizá-los. A GitClear associa exatamente isso ao aumento da duplicação: os assistentes têm "menor probabilidade de propor a reutilização de uma função semelhante… em parte por causa do tamanho limitado do contexto", com blocos duplicados de mais de 5 linhas crescendo cerca de 8× em 2024, enquanto as linhas refatoradas ("movidas") caíram de ~24% para ~9,5%.
  • Bajulação / viés de responder ao prompt. Quando solicitado a "adicionar uma sincronização de pedidos", o modelo entrega algo que funciona agora; externalizar a configuração é um esforço adicional que ele não fará por conta própria a menos que seja instruído.
  • Defasagem do corte de treinamento. Literais embutidos costumam ser literais desatualizados — endpoints antigos, versões de API descontinuadas, portas padrão ou constantes criptográficas fracas (MD5, chaves hardcoded). A OX Security descobriu que 62% do código gerado por IA é entregue com problemas e atribui chaves/segredos/caminhos hardcoded a padrões "aprendidos de bases de código legadas… [com] zero consciência de que os padrões de segurança evoluíram."

Por que isso prejudica

  • Manutenibilidade — Shotgun Surgery. Mudar um único timeout ou URL base significa caçar cada literal duplicado por toda a base de código; se esquecer um, o comportamento diverge silenciosamente em cada ponto de chamada.
  • Correção entre ambientes. Uma URL de staging ou um ID de conta de dev fixado na lógica acaba indo para produção; limiares mágicos sem nome ficam fora de sincronia com os valores que deveriam refletir.
  • Segurança. Tokens/chaves hardcoded são vazamentos de credenciais — uma vez commitados, vivem para sempre no histórico do git. O estudo de código de build do arXiv (2601.16839) encontrou caminhos/URLs hardcoded e credenciais hardcoded de alta severidade recorrentes em arquivos de build gerados por IA.
  • Carga de revisão & dívida técnica. Os revisores precisam verificar a procedência de cada literal. O trabalho sobre smells específicos de IA (arXiv 2509.20491) observa que os modelos lidam bem com "literais dentro do escopo", mas têm dificuldade com casos sensíveis a valores, em que a correção depende de limiares que se propagam por funções auxiliares — exatamente os valores hardcoded que os humanos agora têm que auditar à mão. Segundo o Q&A do Stack Overflow com Eno Reyes, da Factory, a qualidade de base do código é "o único sinal" para saber se os agentes aceleram ou desaceleram uma equipe — e o espalhamento de valores hardcoded corrói exatamente essa base.

##Treatment

Táticas de prompting / revisão

  • Aponte o modelo para sua superfície de configuração: "Leia src/config.ts e .env.example; use config.* / process.env para cada URL, porta, timeout e credencial. Não introduza literais." Os modelos usam literais inline porque não sabem que o símbolo existe — nomeie-o.
  • Proíba valores mágicos explicitamente: "Sem números ou strings mágicos — extraia constantes nomeadas." Depois, faça-o rodar eslint --rule no-magic-numbers e um scanner de segredos (gitleaks/Semgrep) e corrija o que eles sinalizarem. Conforme a orientação da Factory, integre linters/scanners ao loop para que o agente se autocorrija em vez de depender da revisão humana.
  • Forneça o schema de env (zod/envalid/.env.example) para que o modelo tenha onde colocar os valores em vez de adivinhá-los.
  • Faça grep nos novos diffs em busca de http, localhost, literais de IP, Bearer e dígitos soltos em argumentos de chamada antes de fazer o merge.

A refatoração — nomeie os movimentos clássicos:

  • Substituir Número Mágico por Constante Simbólica para limiares, contagens, tamanhos.
  • Extrair Função / Extrair Módulo de Configuração para reunir os parâmetros em um único lugar tipado e validado.
  • Externalizar para o Ambiente (12-factor) para tudo que difere por ambiente ou é secreto; nunca commite segredos.
// config.ts — fonte única e validada da verdade
import { z } from "zod";
const env = z.object({
  ORDERS_API_BASE_URL: z.string().url(),
  ORDERS_API_TOKEN: z.string().min(1),
  ORDERS_TIMEOUT_MS: z.coerce.number().default(30_000),
  ORDERS_PAGE_SIZE: z.coerce.number().default(50),
  ORDERS_MAX_RETRIES: z.coerce.number().default(3),
}).parse(process.env);
export const config = env;
// ✅ depois: referências, não literais — um único lugar para alterar, segredo fora do código-fonte
import { config } from "./config";
export async function syncOrders() {
  const res = await fetch(`${config.ORDERS_API_BASE_URL}/v1/orders`, {
    headers: { Authorization: `Bearer ${config.ORDERS_API_TOKEN}` },
    signal: AbortSignal.timeout(config.ORDERS_TIMEOUT_MS),
  });
  const orders = (await res.json()).slice(0, config.ORDERS_PAGE_SIZE);
  for (let i = 0; i < config.ORDERS_MAX_RETRIES; i++) { /* ... */ }
}

Se um segredo já foi parar em um commit, rotacione-o — remover a linha não apaga o histórico do git.

##Detected by