---
title: "Evitación de la refactorización"
type: "ai-smell"
slug: "refactoring-avoidance"
url: "http://localhost:3000/es/ai-smells/refactoring-avoidance.md"
category: "Estructura"
description: "Los asistentes de IA apilan código nuevo, casi duplicado, junto al que ya existe en lugar de reestructurarlo, de modo que la duplicación crece y la refactorización —el retrabajo que mantiene sana una base de código— desaparece sin que nadie lo note."
---
# Evitación de la refactorización

> Los asistentes de IA apilan código nuevo, casi duplicado, junto al que ya existe en lugar de reestructurarlo, de modo que la duplicación crece y la refactorización —el retrabajo que mantiene sana una base de código— desaparece sin que nadie lo note.

## Signs and Symptoms

Quien revisa ve un diff que es casi enteramente _adiciones_. El modelo resolvió el prompt añadiendo una función, rama o archivo nuevos en vez de editar la abstracción que debería haber absorbido el cambio. Señales reveladoras:

* **Copiar-pegar-con-retoques**: una nueva función es 90% idéntica a una existente, diferenciándose por un literal, un nombre de campo o un `if` de más.
* **Casi-duplicados en paralelo**: `formatUserCsv`, `formatAdminCsv`, `formatGuestCsv`, todos reimplementando a mano el mismo bucle en vez de una sola función parametrizada.
* **Proliferación de ramas en lugar de extracción**: un método largo gana otro `else if` en vez de que el modelo saque una estrategia o tabla de búsqueda.
* **Reinvención**: un `deepClone`/`debounce`/parser de fechas escrito a mano cuando el repositorio ya importa lodash, date-fns, etc. (OX Security lo llama "Estilo vainilla").
* **Sobreespecificación**: una función auxiliar hiperespecífica de un solo uso donde ya existe una genérica dos archivos más allá.
* **"Déjà-vu de fallos"**: el mismo arreglo hay que aplicarlo en tres copias porque el duplicado nunca se aplicó con DRY.

```ts
// Existente en el repositorio:
function priceWithTax(items: Item[]) {
  const subtotal = items.reduce((s, i) => s + i.price * i.qty, 0);
  return subtotal * 1.2; // 20% de IVA
}

// Lo que el modelo añade para el nuevo caso "con descuento":
// una segunda copia entera en vez de un parámetro:
function priceWithTaxDiscounted(items: Item[], discount: number) {
  const subtotal = items.reduce((s, i) => s + i.price * i.qty, 0); // duplicado
  return subtotal * (1 - discount) * 1.2;                          // lógica de IVA duplicada
}

```

El olor es estructural, así que es más visible en el cambio _completo_ que en un solo fragmento: mucho verde, poco código movido, y un escáner de duplicación encendiéndose sobre las nuevas líneas.

## Reasons for the Problem

**Por qué los modelos lo producen**

* **Sesgo aditivo del siguiente token.** Un LLM completa el prompt que tiene delante. Emitir un bloque nuevo y autónomo es la continuación localmente más probable y de menor riesgo; editar una abstracción lejana exige mantener el módulo entero en la memoria de trabajo y predecir una edición globalmente coherente, lo cual es más difícil y no es lo que recompensa el prompt inmediato.
* **Contexto del repositorio escaso.** Los asistentes de programación rara vez cargan la base de código entera. Si el modelo no puede _ver_ la función auxiliar, la biblioteca o la clase base existentes, no puede reutilizarlas, así que las reconstruye. El informe de OX Security enmarca esto como el efecto "Ejército de júniors": mucho código localmente funcional, sin memoria arquitectónica.
* **Refactorizar es arriesgado y no se recompensa.** Reestructurar toca código que al modelo no se le pidió cambiar y puede romper a quienes lo llaman y a los tests. Un asistente adulador, de "tú haz que funcione", minimiza el radio de impacto _no tocando_ el código que ya funciona: justo el comportamiento que OX midió como "Evitación de refactorizaciones" en el 80-90% del código generado por IA, y "Sobreespecificación" (de un solo uso en vez de reutilizable) en otro 80-90%.
* **Obsolescencia de los datos de entrenamiento.** El modelo puede no saber que el repositorio adoptó un módulo de utilidades o actualizó una biblioteca después de su fecha de corte, así que reimplementa a mano lo que ya existe.
* **Generar es barato, borrar da miedo.** Producir 40 líneas nuevas no le cuesta nada al modelo; convencerlo de borrar y consolidar 40 líneas existentes choca con su instinto de preservar.

**Por qué hace daño**

* **La duplicación se acumula.** El análisis de GitClear de 2025 sobre 211M de líneas modificadas halló que las líneas copiadas/pegadas subieron de \~8,3% (2021) a 12,3% (2024) —el primer año en que la duplicación superó al código "movido" (refactorizado)— mientras que las líneas refactorizadas cayeron de \~25% a menos del 10%, una caída de aproximadamente el 60%. Los bloques de más de 5 líneas duplicadas se multiplicaron \~8× en 2024.
* **Mantenibilidad y corrección.** Cada clon es un lugar donde un arreglo futuro puede olvidarse: el "déjà-vu de fallos", donde el mismo defecto reaparece y hay que parchearlo N veces (OX: el 70-80% del código de IA viola así los principios de reutilización).
* **Acumulación de deuda técnica.** La refactorización es el retrabajo que mantiene baja la entropía; suprimirla significa que la deuda se crea pero nunca se paga. La base de código crece más rápido de lo que mejora.
* **Carga de revisión.** Quien revisa debe ahora comparar a ojo bloques casi idénticos para confirmar que son _intencionadamente_ iguales: el desplazamiento de carga cognitiva que describe el debate sobre "olores de código para agentes de IA" de Stack Overflow; el trabajo pasa de escribir a revisar y consolidar.

## Treatment

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

* **Apunta al modelo hacia lo que existe.** "Antes de añadir código, busca en el repositorio una función auxiliar/util/clase base existente y reutilízala; si ninguna encaja, generaliza la más cercana." Pega el módulo relevante en el contexto para que pueda _ver_ de verdad la abstracción.
* **Restringe la forma del diff.** "Prefiere editar funciones existentes a añadir nuevas. Si dos vías de código comparten lógica, extrae una función compartida (Extraer función) en lugar de duplicar."
* **Haz que ejecute las herramientas.** Exige al asistente que ejecute el escáner de duplicación (jscpd / PMD CPD) y el linter, y que resuelva cualquier nuevo hallazgo de bloque duplicado antes de declararlo terminado: esto convierte un olor invisible en una puerta de control que falla.
* **Pide la refactorización de forma explícita como segundo paso.** Generación y consolidación son tareas distintas; haz "que funcione" y luego, por separado, "ahora aplica DRY a esto y elimina la duplicación", algo que los modelos manejan mucho mejor cuando se les pide directamente.
* **Añade una puerta de calidad en CI** para que la duplicación no pueda escalar sin que nadie lo note (umbral de líneas duplicadas de Sonar, o `jscpd --threshold`).

**La refactorización real**: nombra los movimientos clásicos: **Extraer función**, **Parametrizar función** y **Subir / Reemplazar condicional por polimorfismo o una tabla de búsqueda** para matar el **Código duplicado**.

```ts
// Antes: dos funciones casi idénticas (refactorización evitada)
function priceWithTax(items: Item[]) {
  const subtotal = items.reduce((s, i) => s + i.price * i.qty, 0);
  return subtotal * 1.2;
}
function priceWithTaxDiscounted(items: Item[], discount: number) {
  const subtotal = items.reduce((s, i) => s + i.price * i.qty, 0);
  return subtotal * (1 - discount) * 1.2;
}

// Después: Extraer función + Parametrizar función
const VAT = 1.2;
const subtotalOf = (items: Item[]) =>
  items.reduce((s, i) => s + i.price * i.qty, 0);

function priceWithTax(items: Item[], discount = 0) {
  return subtotalOf(items) * (1 - discount) * VAT;
}

```

Una única fuente de verdad para el subtotal y el IVA: un cambio futuro en la regla del impuesto es ahora una edición de una línea en lugar de una búsqueda por N sitios.

## Detected by

- **jscpd** `duplication above --threshold / --min-tokens` — Detección de copiar/pegar (https://github.com/kucherenko/jscpd)
- **PMD** `Copy/Paste Detector (minimum-tokens duplication)` — CPD (https://pmd.github.io/pmd/pmd_userdocs_cpd.html)
- **SonarQube** `duplicated_lines_density / quality-gate duplicated-blocks condition` — Duplicaciones (https://docs.sonarsource.com/sonarqube-server/latest/user-guide/code-metrics/metrics-definition/)
- **ESLint (eslint-plugin-sonarjs)** `sonarjs/no-identical-functions` — no-identical-functions (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-identical-functions.md)
- **ESLint (eslint-plugin-sonarjs)** `sonarjs/no-duplicate-string` — no-duplicate-string (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-duplicate-string.md)
