---
title: "Rueda reinventada"
type: "ai-smell"
slug: "reinvented-wheel"
url: "http://localhost:3000/es/ai-smells/reinvented-wheel.md"
category: "Estructura"
description: "El modelo escribe a mano código a medida para algo que una biblioteca estándar, una dependencia existente o una función auxiliar ya presente en el repositorio ya resuelve, añadiendo lógica duplicada y menos probada en lugar de llamar a lo que ya está ahí."
---
# Rueda reinventada

> El modelo escribe a mano código a medida para algo que una biblioteca estándar, una dependencia existente o una función auxiliar ya presente en el repositorio ya resuelve, añadiendo lógica duplicada y menos probada en lugar de llamar a lo que ya está ahí.

## Signs and Symptoms

Quien revisa detecta una Rueda reinventada cuando un diff introduce un fragmento no trivial de lógica "desde cero" para resolver un problema que ya está resuelto: por el entorno de ejecución del lenguaje, por una dependencia ya presente en `package.json` o por una utilidad que ya existe en otra parte del repositorio. Delatores habituales:

* Un `deepClone`, `debounce`, `groupBy`, `chunk`, `retry`, `slugify`, `deepMerge` o `uuid` escrito a mano cuando el entorno de ejecución o una biblioteca instalada ya lo proporciona.
* Una **expresión regular** a medida para email/URL/UUID en lugar de un validador que ya es una dependencia.
* Aritmética de fechas, parseo de query-string o lógica de paginación personalizados que reimplementan `Intl`/`URLSearchParams`/una característica del ORM.
* Dos o tres funciones auxiliares privadas casi idénticas entre archivos (la misma rueda retallada por cada característica): la duplicación que aparece en las métricas de copiar/pegar.
* La versión reinventada está sutilmente **mal**: maneja el camino feliz pero se le escapan los casos límite que cubre el original probado en batalla.

```ts
// Escrito por IA: un deep clone a medida reinventado en línea
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 en silencio Date, Map, Set, RegExp; entra en bucle con los ciclos
}
// ...aunque el entorno de ejecución incluye structuredClone(), Y
// el repositorio ya exporta cloneDeep() desde src/utils/object.ts

```

La comprobación más rápida: busca en el repositorio y en `node_modules` la capacidad antes de aceptar el código nuevo. Si `structuredClone`, `lodash`, `date-fns` o una función auxiliar local ya lo cubren, la nueva función es una rueda reinventada.

## Reasons for the Problem

**Por qué los modelos lo producen**

* **Sin contexto del repositorio por defecto.** El modelo a menudo no puede ver tu `src/utils`, tus dependencias instaladas ni tus convenciones internas, así que recurre a la continuación estadísticamente más probable: una implementación en línea y autónoma. Reinventa la rueda porque nunca vio la tuya.
* **Localidad del siguiente token.** Los LLM optimizan para una continuación localmente plausible, no para la minimalidad global. Escribir `function groupBy(...)` es una secuencia de alta probabilidad; detenerse a descubrir que `lodash.groupBy` ya está importado tres archivos más allá no es algo que haga la predicción de tokens.
* **Los incentivos de entrenamiento premian la autocontención.** Una porción enorme de los datos de entrenamiento son tutoriales, respuestas de Stack Overflow y fragmentos que muestran deliberadamente la implementación completa. El modelo aprendió que "responder bien" significa emitir código completo e independiente: justo el instinto equivocado dentro de una base de código madura.
* **Obsolescencia por fecha de corte del entrenamiento.** El modelo puede no saber que una capacidad fue promovida a la biblioteca estándar (p. ej. `structuredClone`, `Array.prototype.at`, `Object.groupBy`) o que tu repositorio adoptó una función auxiliar tras su fecha de corte, así que reimplementa con un polyfill algo que ya existe.
* **Adulación / mínima resistencia.** Cuando se le pide "añade X", el modelo añade X de la forma más directa en lugar de replicar con "ya tenemos esto". Rara vez ofrece por iniciativa propia "no necesitas escribir esto".
* **Sobreespecificación.** El análisis de OX Security de más de 300 repositorios halló que la IA tiende a un "estilo vainilla" que reconstruye funcionalidad común en lugar de usar bibliotecas probadas, con soluciones de alcance reducido y no reutilizables en aproximadamente el 80-90% de los casos: cada nueva variación recibe código nuevo en vez de reutilización.

**Por qué hace daño**

* **Mantenibilidad y deuda técnica.** Esta es la cara medible del declive de calidad del código de IA. El análisis de GitClear de 211M de líneas halló que el código copiado/pegado subió de 8,3% (2020) a 12,3% (2024), los bloques de más de 5 líneas duplicadas crecieron \~8× en 2024 y las líneas "movidas" (refactorizadas) cayeron de 24,1% a 9,5%: 2024 fue el primer año en que el copiar/pegar superó a la refactorización. Las ruedas reinventadas son la vía por la que entra esa duplicación.
* **Corrección.** La versión a medida se salta los casos límite que la implementación madura ganó a lo largo de años de informes de fallos (zonas horarias, Unicode, ciclos, escapado). Parece correcta y falla en la cola larga.
* **Seguridad.** Reimplementar criptografía, autenticación, saneamiento o validación en lugar de una biblioteca contrastada es la vía por la que el código escrito por IA "viola las buenas prácticas de ingeniería" a escala (el efecto "Ejército de júniors" de OX): los patrones vulnerables llegan a producción más rápido de lo que la revisión puede atraparlos.
* **Carga de revisión.** Quien revisa ahora tiene que leer, razonar y probar 40 líneas de lógica personalizada en lugar de reconocer una sola llamada a una biblioteca de confianza, multiplicado por cada PR.

## Treatment

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

* **Dale al modelo el contexto que le falta.** Antes de generar, apúntalo a tus utilidades y dependencias: "Reutiliza las funciones auxiliares de `src/utils/*` y las bibliotecas que ya están en `package.json`; no añadas nuevas sin preguntar." Pega las dependencias relevantes de `package.json` y el índice de tus utilidades.
* **Haz de "buscar primero" una regla.** Instruye: "Antes de escribir cualquier función auxiliar, comprueba si la biblioteca estándar, una dependencia existente o una utilidad del repositorio ya lo hacen; si es así, llámala." Las herramientas agénticas deberían hacer grep en el repositorio primero.
* **Cuestiona cada nueva función auxiliar privada en la revisión.** Por cada utilidad escrita a mano pregunta: ¿lo hace el entorno de ejecución? ¿lo hace una dependencia? ¿ya lo tenemos? Si la respuesta es sí, es Rueda reinventada: reemplázala.
* **Ejecuta la cadena de herramientas.** Un escáner de duplicación (jscpd / PMD CPD / SonarQube) en CI marca la faceta de copiar/pegar automáticamente; cablealo en la puerta de control para que las ruedas retalladas hagan fallar la compilación.

**La refactorización**: esto es el clásico **Código duplicado** y **Reinventar la rueda**; el arreglo es **Sustituir algoritmo** (cambiar el cuerpo a medida por la llamada a la biblioteca/estándar) y, donde existan varias copias, **Extraer función** / subir a una única función auxiliar compartida.

```ts
// ANTES — reinventado, parcialmente correcto, duplicado por cada característica
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);

```

```ts
// DESPUÉS — llama a lo que ya existe (casos límite correctos, cero código nuevo)
const copy = structuredClone(state);
// o, si el estándar del repositorio es la función auxiliar local:
import { cloneDeep } from "@/utils/object";
const copy = cloneDeep(state);

```

Si la rueda se reinventó en varios archivos, borra todas las copias y enruta a cada quien la llama a través de la única fuente de verdad. Resultado neto: menos líneas, menos fallos y las métricas de duplicación se mueven en la dirección correcta.

## Detected by

- **jscpd** `duplication` — Detección de copiar/pegar (tokens mínimos / umbral) (https://github.com/kucherenko/jscpd)
- **PMD** `cpd` — CPD (Copy/Paste Detector) (https://docs.pmd-code.org/latest/pmd_userdocs_cpd.html)
- **SonarQube** `common-duplications` — Bloques duplicados / duplicated_lines_density (https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/test-coverage/duplications/)
