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,deepMergeouuidescrito 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.
// 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 quelodash.groupByya 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 enpackage.json; no añadas nuevas sin preguntar." Pega las dependencias relevantes depackage.jsony 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.
// 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);
// 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)
- PMD cpd — CPD (Copy/Paste Detector)
- SonarQube common-duplications — Bloques duplicados / duplicated_lines_density