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.
##Sinais e sintomas
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
Basiccoladas 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.
##Causas do problema
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ãoconfig.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. O modelo normalmente não leu seu
config/, o schema de variáveis de ambiente nem o arquivo de constantes, então não consegue reutilizá-los. O resumo da DevClass sobre a pesquisa da GitClear liga isso ao aumento da duplicação: os assistentes propõem com menos frequência reutilizar uma função parecida, em parte por causa do tamanho limitado do contexto. Os próprios dados da GitClear mostram que blocos com cinco ou mais linhas duplicadas cresceram cerca de oito vezes durante 2024, enquanto as linhas associadas a refatoração ("movidas") caíram de 25% das linhas alteradas em 2021 para menos de 10% em 2024. - 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 pela data de corte do treinamento. Literais embutidos muitas vezes são literais defasados — endpoints antigos, versões de API obsoletas, portas padrão ou constantes criptográficas fracas. O post da OX Security sobre segurança no vibe coding atribui práticas ultrapassadas como o hash MD5 a "padrões aprendidos de bases de código legadas em que essas práticas já foram o padrão do setor" e observa que "o modelo não tem nenhuma consciência de que os padrões de segurança evoluíram".
Por que isso prejudica
- Manutenibilidade — Cirurgia com Espingarda. 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 e chaves hardcoded são vazamentos de credenciais — depois de commitados, ficam para sempre no histórico do git. Um estudo empírico do código de build gerado por IA (arXiv 2601.16839) encontrou 364 smells de código de build em pull requests escritos por agentes, incluindo 25 caminhos/URLs hardcoded e 5 credenciais hardcoded classificadas como de alta severidade; mais de 61% desses pull requests de agentes foram mesclados.
- Carga de revisão e dívida técnica. Os revisores precisam verificar a origem de cada literal, e os valores que controlam o comportamento por meio de funções auxiliares e limiares são justamente os mais difíceis de auditar à mão. No Q&A do Stack Overflow com Eno Reyes, da Factory, Reyes observa que "quanto maior a qualidade da base de código, mais a IA acelerava a organização" — e o espalhamento de valores hardcoded corrói exatamente essa base.
##Tratamento
Táticas de prompting / revisão
- Aponte o modelo para sua superfície de configuração: "Leia
src/config.tse.env.example; useconfig.*/process.envpara 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-numberse 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,Bearere dígitos soltos em argumentos de chamada antes de fazer o merge.
A refatoração — as técnicas do catálogo, mais uma prática de configuração:
- Substituir Número Mágico por Constante Simbólica para limiares, contadores e tamanhos.
- Extrair Classe para reunir as configurações em um único módulo tipado e validado.
- Leve os valores que mudam por ambiente e os segredos para variáveis de ambiente (configuração 12-factor) e leia-os apenas por meio desse módulo; nunca faça commit de 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.
##Detectado por
- ESLint no-magic-numbers — Proibir números mágicos
- SonarSource typescript:S109 / java:S109 — Números mágicos
- SonarSource java:S1075 — URIs hardcoded
- SonarSource java:S2068 — Credenciais hardcoded
- Checkstyle MagicNumber — Verificação de número mágico
- PMD AvoidUsingHardCodedIP — Evitar endereços IP hardcoded
- Bandit B105 hardcoded_password_string / B108 hardcoded_tmp_directory — Senhas hardcoded / diretórios temporários (Python)
- gitleaks generic-api-key — Scanner de segredos hardcoded
- Semgrep generic.secrets / hardcoded-credentials — Regras de segredos/configuração hardcoded
- eslint-plugin-no-secrets no-secrets/no-secrets — Detecção de strings de alta entropia (segredos)