---
title: "Abstracción superficial"
type: "ai-smell"
slug: "shallow-abstraction"
url: "http://localhost:3000/es/ai-smells/shallow-abstraction.md"
category: "Estructura"
description: "Un asistente de IA envuelve el código en funciones, clases o interfaces adicionales que añaden una capa de indirección sin ocultar complejidad alguna ni habilitar la reutilización: abstracciones que se limitan a reenviar a un único punto de llamada."
---
# Abstracción superficial

> Un asistente de IA envuelve el código en funciones, clases o interfaces adicionales que añaden una capa de indirección sin ocultar complejidad alguna ni habilitar la reutilización: abstracciones que se limitan a reenviar a un único punto de llamada.

## Signs and Symptoms

Un revisor reconoce la Abstracción superficial cuando una nueva función/clase/interfaz añade un _nombre y una capa_ pero ningún _aprovechamiento_: su cuerpo repite su firma, reenvía directamente a una única llamada subyacente, se invoca exactamente una vez o no oculta ninguna decisión, invariante ni variación. El código _parece_ estructurado en capas y «de nivel empresarial», pero hay que leer cada capa para entender cualquier cosa: la abstracción no encapsula nada.

Señales reveladoras:

* Funciones/métodos de paso cuyo cuerpo es una única llamada delegada sin comportamiento añadido.
* Clases envoltorio (`FooService` → `FooRepository` → `db`) donde cada capa se limita a llamar a la siguiente.
* Interfaces con una sola implementación (`IClock` con solo `SystemClock`) introducidas de forma especulativa «para facilitar las pruebas» sin una segunda implementación ni un doble.
* Funciones `utils`/`helpers` usadas exactamente en un solo lugar.
* Alias/renombrado redundante que reenvía un valor sin cambios.

Este es el clásico olor a **Intermediario (Middle Man)** / **Clase perezosa (Lazy Class)** / **Generalidad especulativa (Speculative Generality)**, y la otra cara del hallazgo de «sobreespecificación» de OX Security (soluciones hiperespecíficas de un solo uso en lugar de componentes generalizables).

```ts
// La IA añadió tres "capas" que reenvían cada una a la siguiente.
function getUserName(id: string) {
  return fetchUserName(id);            // no aporta nada
}
function fetchUserName(id: string) {
  return userRepository.getName(id);   // no aporta nada
}
class UserRepository {
  getName(id: string) {
    return db.query("SELECT name FROM users WHERE id = ?", [id]);
  }
}
// getUserName es el único punto de llamada, usado una sola vez.
// Dos de las tres capas no aportan ningún comportamiento: puro Intermediario (Middle Man).

```

## Reasons for the Problem

**Por qué los modelos la producen**

* **Imitar la _forma_ de una buena arquitectura.** Los corpus de entrenamiento están saturados de tutoriales y andamiaje empresarial: capas de servicio/repositorio/factoría, inyección de dependencias, interfaces con una sola implementación. El modelo reproduce por coincidencia de patrones la _ceremonia_ de la abstracción sin su sustancia, porque un ayudante con nombre resulta plausible a nivel local y «parece profesional». La generación del siguiente token se optimiza para parecerse a código bien estructurado, no para determinar si una capa justifica su existencia.
* **Sin contexto del repositorio.** Al generar de forma local, el modelo no puede ver que el ayudante se invoca una sola vez, ni que ya existe en otro lugar una abstracción canónica, así que inventa nuevas capas superficiales en lugar de reutilizarlas. Esta es exactamente la tendencia medida por GitClear: las líneas refactorizadas («movidas») cayeron de \~24% (2020) a menos del 10% (2024), mientras las líneas copiadas y pegadas aumentaban y los clones de código crecían \~4 veces. Los modelos añaden estructura, pero nunca la _consolidan_.
* **Fijación con seguir el manual al pie de la letra + sobreespecificación.** OX Security descubrió que la IA sobreespecifica en el 80–90% del código generado, produciendo «soluciones estrechas que no se pueden reutilizar», y evita refactorizaciones en el 80–90% («se detiene en lo suficientemente bueno»). Los envoltorios superficiales son el residuo visible.
* **Literalidad ante las instrucciones y adulación.** Cuando se le pide «añade una capa de servicio» o «hazlo limpio/modular», el modelo añade capas de forma literal aunque aplique YAGNI: la tendencia a la sobreingeniería o de «agente dios» que los profesionales describen en la discusión _code-smells-for-AI-agents_.
* **Obsolescencia por la fecha de corte del entrenamiento.** Envolverá una API obsoleta en un adaptador delgado en lugar de migrar fuera de ella.

**Por qué resulta perjudicial**

* **Mantenibilidad y carga de revisión.** Cada cambio debe atravesar capas muertas; quienes leen el código tienen que recorrer mentalmente los Intermediarios. El argumento del «ejército de júniores» de OX: los revisores no pueden seguir el ritmo de un código que _parece_ estructurado pero no tiene costuras reales.
* **Corrección y seguridad.** Tanto arXiv 2509.20491 como SonarSource señalan que los alias y los envoltorios «ocultan los puntos de llamada decisivos», degradando el análisis de flujo de datos/control y de contaminación (taint): un envoltorio puede tragarse un error en silencio u ocultar un sumidero (sink) a un escáner.
* **Acumulación de deuda técnica.** Las interfaces especulativas con una sola implementación se osifican; y como cada abstracción superficial es de un solo uso, la _siguiente_ variación se copia y pega en lugar de parametrizarse, alimentando directamente el crecimiento de clones que midió GitClear. Pagas por la indirección sin llegar nunca a cobrar la reutilización que prometía.

## Treatment

**Tácticas de revisión y de prompting**

* Aplica la **regla de tres**: "No añadas una función/clase/interfaz a menos que se use en ≥2 lugares _o_ oculte una decisión/invariante real. Incorpora (inline) los ayudantes de un solo uso (YAGNI)."
* Fuerza la reutilización: "Antes de añadir un ayudante/servicio/interfaz, busca uno existente en el repositorio y reutilízalo." Dirige el modelo al módulo canónico.
* Haz que justifique cada capa: "Enumera cada nueva función/clase e indica qué variación o invariante oculta; elimina las que se limiten a reenviar."
* Exígele que **ejecute el linter** (`no-useless-constructor`, `no-useless-rename`) y un **análisis de duplicación** (`jscpd`) y que corrija lo que señalen.

**Refactorizaciones (nómbralas):** **Incorporar función (Inline Function)**, **Incorporar clase (Inline Class)**, **Eliminar intermediario (Remove Middle Man)** para los pasos directos; **Colapsar jerarquía (Collapse Hierarchy)** / eliminar la interfaz en las interfaces con una sola implementación. Cuando la duplicación es el motivo _real_, aplica **Extraer función una sola vez** hacia un ayudante genuinamente compartido y parametrizado, no un envoltorio por cada punto de llamada.

Antes — Intermediario (Middle Man):

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

```

Después — Incorporar función / Eliminar intermediario (un único punto de llamada, sin comportamiento añadido):

```ts
const name = userRepository.getName(id);

```

Antes — Interfaz especulativa con una sola implementación:

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

```

Después — Colapsar jerarquía (usa el tipo concreto; reintroduce la interfaz solo cuando exista realmente una segunda implementación, p. ej. un doble de prueba):

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

```

## Detected by

- **ESLint** `no-useless-constructor` — Constructor de paso que solo delega en super o está vacío (https://eslint.org/docs/latest/rules/no-useless-constructor)
- **ESLint** `no-useless-rename` — Alias redundante de import/export/desestructuración que reenvía un nombre sin cambios (https://eslint.org/docs/latest/rules/no-useless-rename)
- **PMD (Java)** `UselessOverridingMethod` — Método sobrescrito que se limita a llamar a super con los mismos argumentos (https://pmd.github.io/pmd/pmd_rules_java_design.html)
- **SonarSource (Sonar S4144)** `Functions/methods should not have identical implementations` — Señala abstracciones de un solo uso duplicadas con cuerpos idénticos (https://rules.sonarsource.com/java/RSPEC-4144/)
- **jscpd** `duplication threshold` — Detecta los ayudantes de un solo uso casi duplicados que las abstracciones superficiales no consiguen consolidar (https://github.com/kucherenko/jscpd)
