---
title: "Deriva de Convenções"
type: "ai-smell"
slug: "convention-drift"
url: "http://localhost:3000/pt-br/ai-smells/convention-drift.md"
category: "Manutenção"
description: "Código gerado por IA que ignora silenciosamente as convenções estabelecidas de um repositório — reinventando helpers, escolhendo a biblioteca errada e usando nomenclatura e tratamento de erros fora do padrão da casa — fazendo a base de código derivar em direção a um estilo genérico, médio da internet."
---
# Deriva de Convenções

> Código gerado por IA que ignora silenciosamente as convenções estabelecidas de um repositório — reinventando helpers, escolhendo a biblioteca errada e usando nomenclatura e tratamento de erros fora do padrão da casa — fazendo a base de código derivar em direção a um estilo genérico, médio da internet.

## Signs and Symptoms

Um revisor reconhece a Deriva de Convenções quando uma mudança da IA _funciona_, mas não parece pertencer à base de código. Ela recorre ao padrão da média global em vez do padrão local:

* Um novo helper é escrito inline mesmo que já exista um utilitário testado em batalha (`utils/date.ts`, `lib/apiClient`, um tipo `Result` compartilhado).
* Aparece uma biblioteca ou API diferente do padrão do repositório (`axios` em um app que padronizou em um wrapper de `fetch`; `moment` onde o repositório usa `date-fns`).
* Nomenclatura, layout de arquivos e estilo de tratamento de erros divergem — `camelCase` onde o módulo é `snake_case`, `throw new Error("...")` bruto onde todo o resto retorna um erro tipado, `try/catch` sob medida com logging em vez do logger compartilhado.
* Cada novo requisito ganha seu próprio bloco recém-criado e estreitamente ajustado em vez de reutilizar uma abstração ("Sobre-especificação" da Ox Security, vista em 80–90% do código de IA).
* Assinatura sintomática: blocos duplicados se multiplicam. A GitClear mediu um salto de 8× em blocos de clones de 5+ linhas em 2024, com linhas copiadas/coladas ultrapassando linhas movidas (refatoradas) pela primeira vez.

```ts
// Estilo da casa (já no repositório)
import { apiClient } from "@/lib/apiClient";   // encapsula auth, retries, URL base
import { formatDate } from "@/utils/date";      // de todo o app, ciente de locale

// Mudança gerada por IA — deriva para longe de ambos
import axios from "axios";                       // não é um padrão de dependência do projeto
async function getUser(id: string) {
  try {
    const res = await axios.get(`https://api.example.com/users/${id}`); // URL base codificada
    return { ...res.data, joined: new Date(res.data.joined).toLocaleDateString() }; // reinventa formatDate
  } catch (e) {
    console.log("error", e);                     // não é o logger compartilhado; engole o erro
  }
}

```

O indício é a consistência, não a correção: três revisores cada um encontra uma escolha _diferente_ "errada-mas-funcional", e nenhuma delas corresponde ao arquivo ao lado.

## Reasons for the Problem

**Por que os modelos o produzem**

* _Regressão à média do treinamento._ Os LLMs são treinados em um corpus vasto de código médio da internet, não no seu repositório. Quando não recebem instruções, eles emitem a expressão idiomática estatisticamente mais comum (`axios`, `moment`, `console.log`), não a expressão da sua casa — suas convenções são um sinal minúsculo e fora da distribuição perto da média global.
* _Autoconsistência do próximo token em vez de alcance no repositório._ É localmente mais fácil completar um bloco autocontido (embutir um formatador de datas) do que "saber" que `@/utils/date` existe e importar um identificador que o modelo nunca viu. A reutilização exige contexto do repositório que o modelo não detém; a reinvenção só exige o buffer atual.
* _Contexto de repositório limitado / com perdas._ O helper canônico, a configuração de lint e o ADR que diz "use o wrapper do fetch" geralmente estão fora do prompt. O modelo não consegue seguir uma convenção que nunca lhe foi mostrada. Como Eno Reyes, da Factory, colocou, boa parte de uma convenção é _tácita_ — padrões que os humanos absorvem lendo a base de código e que os agentes simplesmente nunca veem.
* _Bajulação / foco literal na tarefa._ Pedido para "adicionar `getUser`", o modelo faz exatamente isso e não se voluntaria a dizer "na verdade, já temos um cliente para isso". Ele otimiza para concluir a tarefa declarada, não para se encaixar no sistema. A "Fixação no Manual" (By-The-Book Fixation) da Ox Security (80–90% das amostras) é a mesma força: ele segue uma convenção genérica de livro-texto em vez de avaliar a do próprio projeto.
* _Defasagem do corte de treinamento._ Se o repositório migrou para uma biblioteca ou padrão mais novo depois do corte do modelo, o modelo reintroduz com confiança a versão na qual foi treinado.

**Por que isso prejudica**

* _Manutenibilidade & carga cognitiva._ Cada escolha derivada é mais uma maneira de fazer a mesma coisa. Os leitores precisam manter N variantes de "como formatamos datas" na cabeça; a base de código perde sua fonte única de verdade.
* _Correção na mudança._ Lógica duplicada/reinventada diverge silenciosamente — um bug corrigido no helper compartilhado _não_ é corrigido na cópia da IA. A GitClear liga a explosão de clones diretamente aos assistentes de IA, que tornam o tab-para-inserir mais barato do que a reutilização, enquanto a participação da refatoração nas mudanças caiu de 25% (2021) para menos de 10% (2024).
* _Segurança._ Validação, autenticação ou construção de queries reinventadas contornam o caminho compartilhado e endurecido (URLs base codificadas, erros engolidos, SQL ad-hoc por string). A Ox chama o efeito agregado de "Exército de Juniores": rápido, funcional, sem julgamento arquitetural.
* _Carga de revisão & acúmulo de dívida técnica._ A deriva não é capturada por testes (o código funciona), então ela aterrissa na revisão ou não é capturada de forma alguma, acumulando-se no smell arquitetural de "Funcionalidade Espalhada / Miragem Modular", onde comportamentos relacionados ficam fragmentados em arquivos sem coesão real.

## Treatment

**Táticas de prompting / fluxo de trabalho**

* _Mostre as convenções._ Coloque as regras onde o agente as lê — um arquivo `CLAUDE.md`/`AGENTS.md`/regras listando o cliente sancionado, o logger, o tipo de erro, a nomenclatura e "use X, não Y". Convenções que o modelo não consegue ver, ele não consegue seguir.
* _Aponte para o código canônico._ "Use `@/lib/apiClient` e `@/utils/date`; não adicione novas bibliotecas HTTP. Combine com o tratamento de erros em `services/orders.ts`." Faça few-shot da expressão idiomática do repositório colando um arquivo exemplar.
* _Pergunte antes de ele escrever._ "Quais helpers/abstrações existentes cobrem isto? Reutilize-os; só adicione código novo se nenhum servir." Isso converte reinvenção em reutilização logo de início.
* _Faça do linter o portão._ Exija que o agente execute o formatador, o linter e o verificador de tipos e corrija todas as constatações antes de retornar. O conselho da Factory é sistematizar os sinais de qualidade (lint/format/type-check/test) para que o agente otimize em direção a eles em vez de derivar.
* _Revise pelo encaixe, não só pela função._ Faça diff dos imports em busca de bibliotecas não sancionadas; faça grep por lógica que deveria ter chamado o helper compartilhado; rode um detector de copy-paste no PR.

**A refatoração** — nomeie os movimentos clássicos: _Remover Duplicação_ / _Substituir Código Inline por Chamada de Função_, _Substituir Algoritmo_ e _Extrair Função_ se a abstração certa ainda não existir.

```ts
// ANTES — derivado, reinventa cliente + lógica de data, URL codificada, erro engolido
import axios from "axios";
async function getUser(id: string) {
  try {
    const res = await axios.get(`https://api.example.com/users/${id}`);
    return { ...res.data, joined: new Date(res.data.joined).toLocaleDateString() };
  } catch (e) {
    console.log("error", e);
  }
}

// DEPOIS — reutiliza as convenções estabelecidas
import { apiClient } from "@/lib/apiClient";
import { formatDate } from "@/utils/date";

async function getUser(id: string) {
  const user = await apiClient.get<User>(`/users/${id}`); // URL base, auth, retries, erros tipados tratados aqui
  return { ...user, joined: formatDate(user.joined) };
}

```

Se o mesmo bloco derivado aparece em vários PRs, esse é um sinal de que a convenção é indescobrível — corrija a causa-raiz documentando-a no arquivo de regras e/ou expondo-a por meio de um ponto de entrada único e óbvio, não revisando cada instância de novo.

## Detected by

- **jscpd** `duplication threshold (min-lines / min-tokens)` — Detecção de copy/paste (https://github.com/kucherenko/jscpd)
- **PMD CPD** `CPD duplicated code blocks` — Detector de Copy-Paste (https://pmd.github.io/latest/pmd_userdocs_cpd.html)
- **ESLint** `no-restricted-imports` — no-restricted-imports (https://eslint.org/docs/latest/rules/no-restricted-imports)
- **typescript-eslint** `@typescript-eslint/naming-convention` — naming-convention (https://typescript-eslint.io/rules/naming-convention/)
- **SonarQube** `Source files should not have any duplicated blocks` — Blocos duplicados (https://docs.sonarsource.com/sonarqube-server/latest/user-guide/code-metrics/metrics-definition/)
