Código repetitivo verboso.
Los asistentes de IA rellenan lógica sencilla con comentarios que repiten cada línea, narración paso a paso, escaleras defensivas de comprobaciones de null y expresiones largas donde bastaría un modismo del lenguaje o un helper existente, inflando el número de líneas sin añadir comportamiento.
##Signos y síntomas
Quien revisa detecta Código repetitivo verboso cuando un diff es mucho más largo de lo que el problema justifica: cada línea está narrada por un comentario que solo la repite, las expresiones simples se envuelven en escaleras defensivas de null/undefined y la misma forma de código reaparece dos o tres veces en vez de factorizarse en una función auxiliar. El delator es una alta proporción comentario-a-lógica y línea-a-comportamiento, además de bloques que podrían haber sido tres líneas de código idiomático.
// Función para obtener el nombre completo del usuario
function getFullName(user: User): string {
// Comprobar si el usuario está definido
if (user === null || user === undefined) {
// Devolver una cadena vacía cuando no se proporciona usuario
return "";
}
// Obtener el nombre, con cadena vacía por defecto
const firstName = user.firstName ? user.firstName : "";
// Obtener el apellido, con cadena vacía por defecto
const lastName = user.lastName ? user.lastName : "";
// Concatenar el nombre y el apellido con un espacio
const fullName = firstName + " " + lastName;
// Recortar los espacios en blanco y devolver el resultado
return fullName.trim();
}
Tres líneas de comportamiento enterradas en ~12 líneas de ceremonia. Otras señales: constructores o envoltorios vacíos que solo delegan, la ceremonia try { ... } catch (e) { throw e }, x ? x : fallback donde bastaría ??, y un validador de email/URL hecho a mano junto al módulo de validación que el proyecto ya tiene. Cuando esa misma forma verbosa aparece además copiada como getDisplayName, getLabel y getInitials, cada una reimplementando lógica que ya cubre una utilidad formatName(), la verbosidad se ha convertido en Código Duplicado: consulta Duplicación en lugar de extracción.
##Causas del problema
Por qué los modelos lo producen
- Sesgo de verbosidad del siguiente token. Los LLM se entrenan con volúmenes enormes de código de tutoriales, Stack Overflow y código principiante donde los comentarios paso a paso y la ceremonia explícita son la norma. La continuación más probable es la más explicada, no la más concisa.
- El RLHF recompensa "parecer minucioso". El ajuste por utilidad/verbosidad empuja a los modelos hacia una salida que aparenta ser completa y autoexplicativa —comentarios en cada línea, comprobaciones defensivas por todas partes— que los evaluadores y los usuarios recompensan aunque no aporte valor.
- Sin contexto del repositorio no hay reutilización. Sin el código circundante en el contexto, el modelo no puede saber que ya existe una utilidad
formatName()ovalidateEmail(), así que vuelve a derivarla en línea. Encaja con dos patrones del informe de OX Security: la sobreespecificación (Over-Specification: "soluciones hiperespecíficas de un solo uso que no pueden adaptarse a otros fines", en el 80–90% del código de IA) y la evitación de refactorizaciones (Avoidance of Refactors, 80–90%). - Generación, no consolidación. Los agentes emiten código nuevo en cada prompt y rara vez vuelven para compactarlo. El análisis de GitClear de 2025 sobre 211 millones de líneas modificadas 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% y en 2024 superaron por primera vez a las movidas; los bloques con cinco o más líneas duplicadas se multiplicaron aproximadamente por ocho durante 2024.
- Comentar todo por defecto. OX Security encontró "Comentarios por todas partes" en el 90-100% del código generado por IA.
Por qué hace daño
- Mantenibilidad: más superficie que leer y cambiar; los comentarios redundantes se desincronizan del código y acaban induciendo a error (el olor Comentarios de Fowler).
- Corrección y seguridad: en cuanto un bloque verboso se copia, un fallo o una vulnerabilidad debe corregirse en N sitios: el patrón Bugs Déjà-Vu de OX (70–80%), que hace que "los mismos fallos aparezcan una y otra vez en el código y requieran la misma corrección varias veces".
- Carga de revisión: los diffs grandes y de baja señal ocultan los cambios reales e inducen fatiga en quien revisa, así que los problemas genuinos se cuelan.
- Deuda técnica acumulativa: cada casi-duplicado hace más difícil la siguiente extracción, atrincherando el problema que el modelo no puede ver entre archivos.
##Tratamiento
Tácticas de revisión y de prompts
- Fuerza la reutilización antes de generar: "Busca en la base de código funciones auxiliares existentes (
formatName,validate*) y reutilízalas; no las reimplementes." Proporciona las utilidades relevantes en el contexto. - Pon un presupuesto a la salida: "Mantén esto por debajo de ~15 líneas; sin comentarios que repitan el código, solo comentarios que expliquen el porqué."
- Haz que el modelo ejecute las herramientas: exige
eslint/ruffy una pasada de duplicación conjscpd, y que informe y arregle las violaciones antes de devolver. - Pide el diff más pequeño y un paso explícito de deduplicación: "Si algún bloque se repite, extrae una función compartida y llámala" (Extraer método).
- Consolida en la revisión: cuando veas la tercera casi-copia, aplica Extraer método, Subir Método o Consolidar fragmentos condicionales duplicados; donde un comentario solo explica qué hace una expresión, aplica Extraer Variable o Renombrar método para que sea el código el que lleve la explicación y el comentario pueda desaparecer.
Refactorización (antes → después)
// después: comentarios eliminados (el código se explica solo), ceremonia colapsada,
// y se reutiliza la utilidad formatName() existente en vez de rederivarla en línea
import { formatName } from "@/utils/format";
export const getFullName = (user?: User): string =>
formatName(user?.firstName, user?.lastName);
Y elimina los duplicados de raíz: si getDisplayName/getLabel hacían lo mismo, dirige a quienes las llaman a una sola función y borra las copias (Método en Línea); lo que quede sin llamadas es Código muerto y también sobra. Sustituye el validador hecho a mano por la importación del módulo existente en lugar de mantener una copia paralela.
Regla práctica para el catálogo: trata cualquier bloque de IA donde los comentarios superen en número a las líneas de lógica, o donde puedas nombrar una función auxiliar existente que ignoró, como candidato a la eliminación-por-reutilización: el arreglo más rápido del código repetitivo suele ser menos código, no más.
##Detectado por
- ESLint (core) no-useless-catch — Cláusulas catch innecesarias (ceremonia de capturar y relanzar: catch (e) { throw e })
- ESLint (core) no-unneeded-ternary — Operadores ternarios donde existe una alternativa más simple (con defaultAssignment: false detecta x ? x : fallback)
- typescript-eslint @typescript-eslint/prefer-nullish-coalescing — Preferir ?? frente a || y a ternarios con comprobación explícita de null
- ESLint (core) no-useless-constructor — Constructor vacío redundante / código repetitivo de paso directo
- ESLint (core) max-lines-per-function — Longitud de función inflada/verbosa (indicador parcial)
- jscpd --min-tokens / --threshold — Detección de copiar/pegar, para cuando los bloques verbosos se copian y pegan (umbral --min-tokens / --threshold)
- SonarQube / SonarCloud typescript:S4144 — Las funciones y los métodos no deben tener implementaciones idénticas
- ESLint (eslint-plugin-sonarjs) sonarjs/no-identical-functions — Las funciones no deben tener implementaciones idénticas