ConstructiCat Logo
CodeBust.
Browse section ▾

Roda Reinventada.

O modelo escreve à mão código sob medida para algo que uma biblioteca padrão, uma dependência existente ou um helper já presente no repositório já faz — adicionando lógica duplicada e menos testada em vez de chamar o que já existe.

##Signs and Symptoms

Um revisor identifica a Roda Reinventada quando um diff introduz um trecho não trivial de lógica "do zero" para resolver um problema que já está resolvido — pelo runtime da linguagem, por uma dependência já presente em package.json ou por um utilitário que já existe em outro lugar do repositório. Sinais comuns:

  • Um deepClone, debounce, groupBy, chunk, retry, slugify, deepMerge ou uuid escrito à mão quando o runtime ou uma biblioteca instalada já o fornece.
  • Uma regex sob medida de e-mail/URL/UUID em vez de um validador que já é uma dependência.
  • Cálculos de data personalizados, parsing de query-string ou lógica de paginação que reimplementam Intl/URLSearchParams/um recurso de ORM.
  • Dois ou três helpers privados quase idênticos espalhados por arquivos (a mesma roda re-esculpida por funcionalidade) — a duplicação que aparece nas métricas de copiar/colar.
  • A versão reinventada está sutilmente errada: ela trata o caminho feliz, mas deixa passar casos extremos que o original testado em batalha cobre.
// Escrito por IA: um deep clone sob medida reinventado inline
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (Array.isArray(obj)) return obj.map(deepClone);
  const out = {};
  for (const k in obj) out[k] = deepClone(obj[k]);
  return out; // descarta silenciosamente Date, Map, Set, RegExp; entra em loop em ciclos
}
// ...mesmo que o runtime já forneça structuredClone(), E
// o repositório já exporta cloneDeep() de src/utils/object.ts

A verificação mais rápida: procure no repositório e em node_modules pela capacidade antes de aceitar o novo código. Se structuredClone, lodash, date-fns ou um helper local já o cobre, a nova função é uma roda reinventada.

##Reasons for the Problem

Por que os modelos produzem isso

  • Sem contexto do repositório por padrão. O modelo muitas vezes não consegue ver seu src/utils, suas dependências instaladas ou as convenções da casa, então recorre à conclusão estatisticamente mais provável: uma implementação inline autocontida. Ele reconstrói a roda porque nunca viu a sua.
  • Localidade do próximo token. LLMs otimizam para uma continuação localmente plausível, não para a minimalidade global. Escrever function groupBy(...) é uma sequência de alta probabilidade; parar para descobrir que lodash.groupBy já está importado a três arquivos de distância não é algo que a previsão de tokens faça.
  • Os incentivos de treinamento recompensam a autossuficiência. Uma enorme parcela dos dados de treinamento são tutoriais, respostas do Stack Overflow e trechos que deliberadamente mostram a implementação completa. O modelo aprendeu que "responder bem" significa emitir código completo e autônomo — exatamente o instinto errado dentro de uma base de código madura.
  • Defasagem do corte de treinamento. O modelo pode não saber que uma capacidade foi promovida à biblioteca padrão (por exemplo, structuredClone, Array.prototype.at, Object.groupBy) ou que seu repositório adotou um helper após seu corte, então ele faz polyfill de algo que já existe.
  • Bajulação / menor resistência. Solicitado a "adicionar X," o modelo adiciona X da maneira mais direta em vez de contestar com "nós já temos isso." Ele raramente sugere espontaneamente "você não precisa escrever isso."
  • Superespecificação. A análise da OX Security de mais de 300 repositórios constatou que a IA tende a um estilo de código "vanilla" que reconstrói funcionalidades comuns em vez de usar bibliotecas comprovadas, com soluções de escopo restrito e não reutilizáveis em cerca de 80–90% dos casos — cada nova variação ganha código novo em vez de reutilização.

Por que isso é prejudicial

  • Manutenibilidade e dívida técnica. Esta é a face mensurável do declínio na qualidade do código de IA. A análise da GitClear sobre 211 milhões de linhas constatou que o código copiado/colado subiu de 8,3% (2020) para 12,3% (2024), os blocos de 5+ linhas duplicadas cresceram ~8x em 2024, e as linhas "movidas" (refatoradas) caíram de 24,1% para 9,5% — 2024 foi o primeiro ano em que o copiar/colar superou a refatoração. As rodas reinventadas são por onde essa duplicação entra.
  • Correção. A versão sob medida pula os casos extremos que a implementação madura conquistou ao longo de anos de relatórios de bugs (fusos horários, Unicode, ciclos, escape). Parece certa e falha na cauda longa.
  • Segurança. Reescrever criptografia, autenticação, sanitização ou validação em vez de usar uma biblioteca confiável é como o código escrito por IA "viola as melhores práticas de engenharia" em escala (o efeito "Exército de Juniores" da OX) — padrões vulneráveis chegam à produção mais rápido do que a revisão consegue detectá-los.
  • Carga de revisão. Os revisores agora precisam ler, raciocinar sobre e testar 40 linhas de lógica personalizada em vez de reconhecer uma única chamada de biblioteca confiável — multiplicado em cada PR.

##Treatment

Táticas de revisão e de prompting

  • Dê ao modelo o contexto que lhe falta. Antes de gerar, aponte-o para seus utilitários e dependências: "Reutilize helpers de src/utils/* e bibliotecas já presentes em package.json; não adicione novas sem perguntar." Cole as dependências relevantes do package.json e o índice dos seus utils.
  • Faça do "buscar primeiro" uma regra. Instrua: "Antes de escrever qualquer helper, verifique se a biblioteca padrão, uma dependência existente ou um utilitário do repositório já faz isso; em caso afirmativo, chame-o." Ferramentas agênticas devem fazer grep no repositório primeiro.
  • Questione cada novo helper privado na revisão. Para cada utilitário feito à mão pergunte: o runtime faz isso? uma dependência faz isso? nós já temos isso? Se sim, é Roda Reinventada — substitua-a.
  • Execute a toolchain. Um scanner de duplicação (jscpd / PMD CPD / SonarQube) na CI sinaliza a faceta de copiar/colar automaticamente; conecte-o ao portão para que rodas re-esculpidas façam o build falhar.

A refatoração — isto é o clássico Código Duplicado e Reinventar a Roda; a correção é Substituir Algoritmo (trocar o corpo sob medida pela chamada da biblioteca/padrão) e, onde existem várias cópias, Extrair Função / subir para um único helper compartilhado.

// ANTES — reinventado, parcialmente correto, duplicado por funcionalidade
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (Array.isArray(obj)) return obj.map(deepClone);
  const out = {};
  for (const k in obj) out[k] = deepClone(obj[k]);
  return out;
}
const copy = deepClone(state);
// DEPOIS — chame o que já existe (casos extremos corretos, zero código novo)
const copy = structuredClone(state);
// ou, se o padrão do repositório for o helper local:
import { cloneDeep } from "@/utils/object";
const copy = cloneDeep(state);

Se a roda foi reinventada em vários arquivos, apague todas as cópias e direcione cada chamador para a única fonte de verdade. Resultado líquido: menos linhas, menos bugs e as métricas de duplicação voltam na direção certa.

##Detected by

  • jscpd duplicationDetecção de copiar/colar (min-tokens / limite)
  • PMD cpdCPD (Copy/Paste Detector)
  • SonarQube common-duplicationsBlocos duplicados / duplicated_lines_density