ConstructiCat Logo
CodeBust.
Browse section ▾

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

// 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 duplicationDetección de copiar/pegar (tokens mínimos / umbral)
  • PMD cpdCPD (Copy/Paste Detector)
  • SonarQube common-duplicationsBloques duplicados / duplicated_lines_density