Logotipo de ConstructiCat
CodeBust.
Explorar la sección ▾

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í.

##Signos y síntomas

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.
// 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.

##Causas del problema

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".
  • Vanilla Style y sobreespecificación. El análisis de OX Security de más de 300 repositorios describe dos patrones relacionados: Vanilla Style, en el que la IA "reimplementa la funcionalidad desde cero" en lugar de usar bibliotecas consolidadas (40–50% del código generado por IA), y la sobreespecificación (Over-Specification): "soluciones hiperespecíficas de un solo uso que no pueden adaptarse a otros fines" (80–90%). Juntos hacen que cada nueva variación reciba código nuevo en lugar 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 2025 sobre 211 millones de líneas modificadas (2020–2024) halló que las líneas asociadas a refactorización ("movidas") cayeron del 25% de las líneas cambiadas en 2021 a menos del 10% en 2024, mientras que las copiadas y pegadas subieron del 8,3% al 12,3% en el mismo periodo; 2024 fue el primer año en que las líneas copiadas superaron a las movidas, y los bloques con cinco o más líneas duplicadas se multiplicaron aproximadamente por ocho durante 2024. Las ruedas reinventadas son una de las vías por las 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.

##Tratamiento

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 más el antipatrón de reinventar la rueda; el arreglo es Sustituir algoritmo (cambiar el cuerpo hecho a medida por la llamada a la biblioteca o al estándar) y, donde existan varias copias, Extraer método / Subir Método hacia 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.

##Detectado por