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.
##Signs and Symptoms
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 (
IClockapenas comSystemClock) introduzidas especulativamente "para testabilidade" sem uma segunda implementação ou fake. - Funções
utils/helpersusadas em exatamente um lugar. - Aliasing/renomeação redundante que repassa um valor inalterado.
Esse é o clássico smell Middle Man / Lazy Class / Speculative Generality, e o outro lado da constatação de "superespecificação" da OX Security (soluções hiperespecíficas e de uso único em vez de componentes generalizáveis).
// 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.
##Reasons for the Problem
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 consegue ver que o auxiliar é chamado uma única vez, ou que já existe uma abstração canônica em outro lugar — então inventa novas camadas rasas em vez de reutilizá-las. Essa é exatamente a tendência medida pela GitClear: linhas refatoradas ("movidas") caíram de ~24% (2020) para menos de 10% (2024), enquanto as linhas copiadas/coladas aumentaram e os clones de código cresceram ~4x. Os modelos adicionam estrutura, mas nunca a consolidam.
- Fixação no "manual" + superespecificação. A OX Security constatou que a IA superespecifica em 80–90% do código gerado, produzindo "soluções estreitas que não podem ser reutilizadas", e evita refatorações em 80–90% dos casos ("para no suficientemente bom"). Os wrappers rasos são o resíduo visível.
- Literalismo no prompt e bajulação. Instruído a "adicionar uma camada de serviço" ou "deixar limpo/modular", o modelo adiciona camadas literalmente mesmo quando vale o YAGNI — a tendência de superengenharia / "god agent" que os profissionais descrevem na discussão code-smells-for-AI-agents.
- 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ê precisa percorrer Middle Men inline. O argumento do "Exército de Juniores" da OX: os revisores não conseguem acompanhar código que parece estruturado mas não tem costuras reais.
- Correção e segurança. O arXiv 2509.20491 e a SonarSource observam que o aliasing e os wrappers "obscurecem os pontos de chamada decisivos", degradando a análise de fluxo de dados/controle e de taint — um wrapper pode engolir silenciosamente um erro ou esconder um sink de um scanner.
- 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.
##Treatment
Táticas de revisão e de prompting
- Aplique a Regra de Três: "Não adicione uma função/classe/interface a menos que ela seja usada em ≥2 lugares ou esconda uma decisão/invariante real. Faça inline dos auxiliares de uso único (YAGNI)."
- 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 (nomeie-as): Inline Function, Inline Class, Remove Middle Man para repasses; Collapse Hierarchy / descarte a interface no caso de interfaces de implementação única. Quando a duplicação é o motivo real, faça Extract Function uma vez para um auxiliar genuinamente compartilhado e parametrizado — não um wrapper por ponto de chamada.
Antes — Middle Man:
function getUserName(id: string) { return fetchUserName(id); }
function fetchUserName(id: string) { return userRepository.getName(id); }
Depois — Inline Function / Remove Middle Man (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 — Collapse Hierarchy (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(); } }
##Detected by
- 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) Functions/methods should not have identical implementations — Sinaliza abstrações de uso único duplicadas com corpos idênticos
- jscpd duplication threshold — Detecta os auxiliares de uso único quase duplicados que as abstrações rasas não conseguem consolidar