Logotipo do ConstructiCat
CodeBust.
Explorar a seção ▾

Abstração Rasa.

Um assistente de IA envolve o código em funções, classes ou interfaces extras que adicionam uma camada de indireção sem esconder nenhuma complexidade nem permitir reutilização — abstrações que apenas repassam para um único ponto de chamada.

##Sinais e sintomas

Um revisor reconhece a Abstração Rasa quando uma nova função/classe/interface adiciona um nome e uma camada mas nenhuma alavancagem: seu corpo apenas repete sua assinatura, repassa direto para uma única chamada subjacente, é invocada exatamente uma vez ou não esconde nenhuma decisão, invariante ou variação. O código parece ter camadas e um ar "corporativo", mas você precisa ler todas as camadas para entender qualquer coisa — a abstração não encapsula nada.

Sinais reveladores:

  • Funções/métodos de repasse cujo corpo é uma única chamada delegante sem nenhum comportamento adicional.
  • Classes wrapper (FooService → FooRepository → db) em que cada camada apenas chama a próxima.
  • Interfaces de implementação única (IClock apenas com SystemClock) introduzidas especulativamente "para testabilidade" sem uma segunda implementação ou fake.
  • Funções utils/helpers usadas em exatamente um lugar.
  • Aliasing/renomeação redundante que repassa um valor inalterado.

Esse é o clássico smell Intermediário / Classe Preguiçosa / Generalidade Especulativa, e o outro lado da constatação de superespecificação da OX Security ("soluções hiperespecíficas, de uso único, que não podem ser adaptadas para outros fins").

// A IA adicionou três "camadas" que apenas repassam para a próxima.
function getUserName(id: string) {
  return fetchUserName(id);            // não acrescenta nada
}
function fetchUserName(id: string) {
  return userRepository.getName(id);   // não acrescenta nada
}
class UserRepository {
  getName(id: string) {
    return db.query("SELECT name FROM users WHERE id = ?", [id]);
  }
}
// getUserName é o único ponto de chamada, usado uma vez.
// Duas das três camadas não adicionam nenhum comportamento — puro Middle Man.

##Causas do problema

Por que os modelos a produzem

  • Imitar a forma de uma boa arquitetura. Os corpora de treinamento estão saturados de tutoriais e scaffolding corporativo — camadas de service/repository/factory, DI, interfaces com uma única implementação. O modelo reconhece o cerimonial da abstração sem a substância, porque um auxiliar nomeado é localmente plausível e "parece profissional". A geração do próximo token otimiza para parecer código bem estruturado, não para saber se uma camada justifica sua existência.
  • Sem contexto do repositório. Gerando localmente, o modelo não vê que o helper é chamado uma única vez, nem que já existe uma abstração canônica em outro lugar — então inventa novas camadas rasas em vez de reutilizar. A análise de 2025 da GitClear mediu a mesma tendência: as linhas associadas a refatoração ("movidas") caíram de 25% das linhas alteradas em 2021 para menos de 10% em 2024, enquanto as copiadas e coladas subiram de 8,3% para 12,3% — o que a GitClear resume na manchete como um crescimento de 4x nos clones de código. Os modelos adicionam estrutura, mas raramente a consolidam.
  • Apego ao manual + superespecificação. O relatório da OX Security sobre mais de 300 repositórios lista a fixação no manual (By-The-Book Fixation) — código que "segue rigidamente padrões convencionais mesmo quando soluções mais inovadoras seriam mais eficazes" — e a superespecificação (Over-Specification) — "soluções hiperespecíficas, de uso único, que não podem ser adaptadas para outros fins" —, cada uma em 80–90% do código gerado por IA, além da evitação de refatorações (Avoidance of Refactors, também 80–90%): a IA "se destaca gerando código novo, mas nunca volta para melhorar a arquitetura". Wrappers rasos são o resíduo visível.
  • Literalismo do prompt e bajulação. Quando recebe "adicione uma camada de serviço" ou "deixe limpo/modular", o modelo adiciona camadas ao pé da letra mesmo quando YAGNI se aplica — superengenharia por pedido, não por necessidade.
  • Desatualização pelo corte do treinamento. Ele envolve uma API obsoleta em um adaptador fino em vez de migrar para fora dela.

Por que isso prejudica

  • Manutenibilidade e carga de revisão. Toda mudança precisa atravessar camadas mortas; quem lê tem que passar por cada Intermediário para chegar à chamada real. A OX compara a IA a "um exército de desenvolvedores juniores talentosos, mas inexperientes", cuja produção supera a capacidade de revisão — e código que parece estruturado mas não tem costuras reais é justamente o que passa.
  • Correção e segurança. Cada wrapper e cada alias é mais um salto entre a entrada e a chamada que importa: o revisor precisa segui-lo, e qualquer análise de fluxo de dados ou de taint também. Uma função de passagem que "não acrescenta nada" ainda pode engolir um erro ou esconder um sink atrás de um nome inofensivo.
  • Acúmulo de dívida técnica. Interfaces especulativas de uma única implementação se ossificam; e como cada abstração rasa é de uso único, a próxima variação é copiada e colada em vez de parametrizada — alimentando diretamente o crescimento de clones que a GitClear mediu. Você paga pela indireção sem nunca colher a reutilização que ela prometia.

##Tratamento

Táticas de revisão e de prompting

  • Aplique YAGNI e a regra de três: "Não adicione uma função/classe/interface para reutilização até que três pontos de chamada realmente precisem dela; abaixo disso, uma nova camada precisa esconder uma decisão ou um invariante reais. Faça inline dos helpers de uso único."
  • Force a reutilização: "Antes de adicionar um auxiliar/serviço/interface, procure no repositório por um já existente e reutilize-o." Aponte o modelo para o módulo canônico.
  • Faça-o justificar cada camada: "Liste cada nova função/classe e diga qual variação ou invariante ela esconde; remova as que apenas repassam."
  • Exija que ele execute o linter (no-useless-constructor, no-useless-rename) e uma varredura de duplicação (jscpd) e corrija o que for sinalizado.

Refatorações (dê nome a elas): Embutir Método, Classe Inline, Remover Intermediário para funções de passagem; Recolher Hierarquia ou remover a interface para interfaces de implementação única. Quando a duplicação é o motivo real, aplique Extrair Método uma única vez para criar um helper realmente compartilhado e parametrizado — não um wrapper por ponto de chamada.

Antes — Intermediário:

function getUserName(id: string) { return fetchUserName(id); }
function fetchUserName(id: string) { return userRepository.getName(id); }

Depois — Embutir Método / Remover Intermediário (um ponto de chamada, sem comportamento adicional):

const name = userRepository.getName(id);

Antes — interface especulativa de implementação única:

interface IClock { now(): number; }
class SystemClock implements IClock { now() { return Date.now(); } }

Depois — Recolher Hierarquia (use o tipo concreto; reintroduza a interface apenas quando uma segunda implementação, por exemplo um fake de teste, realmente existir):

class SystemClock { now() { return Date.now(); } }

##Detectado por

  • ESLint no-useless-constructor — Construtor de repasse que apenas delega para super ou está vazio
  • ESLint no-useless-rename — Aliasing redundante de import/export/desestruturação que repassa um nome inalterado
  • PMD (Java) UselessOverridingMethod — Método sobrescrito que apenas chama super com os mesmos argumentos
  • SonarSource (Sonar S4144) S4144 — Sinaliza abstrações de uso único duplicadas com corpos idênticos
  • jscpd --threshold — Detecta os auxiliares de uso único quase duplicados que as abstrações rasas não conseguem consolidar