Las dos de la madrugada. Llevas un rato mirando fijamente la línea if is_premium and not is_trial and billing_cycle == 'annual' en el diff, con el cursor suspendido sobre «Request Changes» sin atreverte a pulsar. El título de la Pull Request es «Unificar la lógica de cálculo de descuento de customer y broker». Los dos bloques de código son casi idénticos: cargar un registro, actualizar un campo de porcentaje, volver a escribir en la base de datos. Un ingeniero detectó la «duplicación» y extrajo un método unificado con un parámetro entity_type. Parece limpio, razonable y DRY.
Pero tú sabes que el descuento de customer va a recalcularse mañana mismo por tramos de precio, mientras que la lógica de comisiones de broker no cambiará en dos años. Forzar ahora la unificación elimina la duplicación en apariencia, pero en realidad suelda con un soplete dos conceptos cuyas trayectorias de evolución son completamente divergentes. Esta es exactamente la trampa contra la que Sandi Metz alertó hace una década. En junio de 2026 su artículo volvió al número uno de HN con 409 puntos y 272 comentarios: en una era en la que una IA puede generarte quinientas líneas de código «con buena pinta» de una sola tacada, el principio merece ser rediscutido con más urgencia que nunca.
Sandi Metz dibujó un mapa de la putrefacción
Metz pronunció por primera vez la frase «duplication is far cheaper than the wrong abstraction» en su charla de la RailsConf 2014, y en 2016 la convirtió en artículo de blog. Su argumentación es de una sencillez aplastante y no se apoya en ningún marco teórico: se limita a describir un proceso de degeneración que todo el mundo ha vivido pero casi nadie había bautizado.
El programador A descubre código duplicado. Extrae un método o clase común, reemplaza todas las repeticiones y se marcha satisfecho. Pasa el tiempo, llegan nuevos requisitos. La abstracción existente «casi» sirve. El programador B hereda el código y, por respeto a lo ya hecho, evita empezar de cero: añade un parámetro al método y luego una rama condicional dentro del método. Después vienen el tercer requisito, el cuarto parámetro, el quinto if-else. Para el octavo paso apareces tú, frente a miles de líneas de lógica condicional entrelazada, tratando de entender qué rama pertenece a qué invocador.
La receta de Metz es igual de concisa: revierte la abstracción, inlinea todo para que cada invocador se quede solo con el código que realmente necesita, y después observa desde cero. Qué similitudes son auténticas y cuáles son meramente «aparentes».
Si estas palabras tienen poder de penetración es porque rompen una creencia casi religiosa dentro de la comunidad de programadores: que la duplicación es el mal y que eliminarla es un bien axiomático.
La mochila histórica de DRY: de las bases de datos a los repositorios de código
El principio DRY fue formulado por Andy Hunt y Dave Thomas en «The Pragmatic Programmer» (1999). La redacción original dice: «Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.» El acento está en «conocimiento», no en «caracteres». Una consulta SQL, una regla de negocio, un valor de configuración: eso es conocimiento. Dos bucles for que casualmente se parecen pueden no serlo en absoluto.
Pero la industria fue comprimiendo esa distinción a medida que difundía el principio. «No te repitas» se convirtió en «no puede haber líneas de código repetidas». Una heurística elevada a regla pétrea alumbró infinidad de abstracciones que no deberían haber existido: clases Repository genéricas, métodos Processor comodín, funciones de servicio cuya lista de parámetros era más larga que la propia lógica de negocio.
Lo que hace Metz es, en esencia, recalibrar DRY: su blanco es el «DRY prematuro». Esta puntualización se repitió una y otra vez en el hilo de HN: «El artículo no dice que no abstraigas; dice que no fuerces la abstracción.»
Señales de ingeniería para detectar la «abstracción equivocada»
En los comentarios de HN, varios ingenieros compartieron sus criterios personales para identificar una abstracción equivocada. «¿Este código hace lo mismo o solo se parece?» fue la piedra de toque más citada. Cuando confluyen varias de las siguientes señales, la probabilidad de que la abstracción esté mal es alta:
Ramas condicionales pilotadas por parámetros. Un método recibe un booleano o un enumerado y por dentro bifurca con if-else hacia caminos de código que apenas se solapan. Cada nuevo parámetro multiplica por una capa adicional el espacio de estados que el invocador tiene que comprender.
Modificar el comportamiento de un invocador te obliga a añadir código de «resguardo» para los demás. Significa que entre los invocadores no existe una verdadera relación de covarianza: simplemente hoy ejecutan código parecido por casualidad.
La abstracción no tiene una razón de ser autoevidente. Una abstracción sana se entiende sin necesidad de consultar a sus invocadores. Si cada vez que la lees tienes que remontarte al contexto de tres sitios de llamada para entender qué hace esa lógica, la abstracción ya ha perdido su mayor valor: reducir la carga cognitiva.
Cuando añades una funcionalidad nueva, tu primer impulso es rodear la abstracción, no reutilizarla. Esta es la señal más fiable. La intuición suele captar problemas estructurales antes que la racionalización a posteriori.
El debate central en HN: verdad única frente a localidad
Dos comentarios muy votados delimitaron con precisión las fronteras de la controversia.
El usuario lg5689 defendió el núcleo del bando «primero la abstracción»: «Siempre hay que seguir el principio de fuente única de verdad. Si dos fragmentos duplicados, al divergir, constituirían un bug, hay que refactorizar. La duplicación crea un acoplamiento invisible de larga distancia en el código.» La lógica responde a una intuición de ingeniería limpia: cuando la misma regla de negocio está repartida en dos sitios, el día que modifiques uno y olvides el otro habrás plantado un bug.
La réplica del usuario jonahx apunta al escenario que realmente ocupa a Metz: «En el fondo, el artículo habla precisamente de cuando todavía no sabes cuántas fuentes de verdad hay. ¿Esos dos sitios usan el mismo algoritmo o versiones ligeramente distintas? Y, más importante aún, ¿van a cambiar por el mismo tipo de razones? Lo crucial es que una abstracción equivocada destruye la localidad, y la localidad es prácticamente la única propiedad que de verdad te importa cuando modificas código. Yo solo quiero hacer este cambio, sin preocuparme por los efectos secundarios en partes no relacionadas del sistema.»
Ambos comentarios son razonables, pero se aplican a escenarios distintos. Si tienes certeza de que dos sitios representan el mismo hecho inmutable —el mismo tipo impositivo, el mismo algoritmo criptográfico, la misma regla de validación— entonces la abstracción es la decisión correcta: los beneficios de la fuente única de verdad superan con creces el coste de la propia abstracción.
El problema es que, en esta industria, hemos sobrestimado nuestra capacidad para «ver si dos fragmentos de código son semánticamente idénticos». El ejemplo de Metz lo ilustra: los dos cálculos se parecen mucho —cargar registro de customer, actualizar porcentaje de descuento; cargar registro de broker, actualizar porcentaje de comisión—. Ambos encajan hoy en el patrón «cargar entidad – actualizar porcentaje». Pero la lógica del descuento de customer puede virar mañana a un cálculo por tramos mientras la comisión de broker se queda en un porcentaje único, porque la naturaleza jurídica, contractual y contable de ambos campos es completamente distinta.
La línea que separa «código que se parece» de «código que representa la misma verdad» es mucho más difícil de trazar de lo que la mayoría de los ingenieros estamos dispuestos a admitir.
Cómo la generación de código con IA amplifica este problema
Justo aquí está la razón de que el artículo de Metz haya vuelto a la cima tras la adopción masiva de herramientas de programación con IA.
Los LLM presentan dos sesgos estructurales al generar código. El primero es su tendencia natural a «eliminar la duplicación aparente». Cuando con un mismo prompt generas dos módulos de funcionalidad similar, el modelo extrae de sus datos de entrenamiento la forma «estándar» de unificarlos y te devuelve una abstracción parametrizada. No te pregunta cuál es la frontera de negocio entre customer y broker: el modelo no ha participado en ninguna reunión de requisitos. Sencillamente ha encontrado la representación compartida estadísticamente óptima.
El segundo, más sutil y peligroso, es que las abstracciones generadas por LLM son anormalmente lisas. El naming es correcto, la indentación impecable, la disposición de parámetros tiene un aire de lógica aplastante. Una abstracción chapucera escrita por un humano suele oler: el nombre es forzado, la estructura es endeble, se nota que el código se retuerce para caber. La abstracción equivocada producida por un LLM tiene un aspecto profesional, seguro de sí mismo, blindado. Al revisor le cuesta más rechazarla.
Varios comentaristas de HN señalaron esta tensión. Alguien dijo que «los LLM son máquinas antiabstracción por naturaleza», porque no entienden la semántica de negocio, solo los patrones de superficie. Otro añadió que «los LLM han abaratado tanto el coste de la copia que el listón argumental para justificar una abstracción debería ser mucho más alto». Una observación aún más afilada: «En lo que más tiempo me paso pensando es en cómo explicarle a un LLM cómo funciona realmente un código legacy sin que lo deforme por un malentendido.»
Hay un fenómeno de ingeniería llamativo: grandes volúmenes de código generado por IA tienden a la copia en vez de a la abstracción. La razón no es que el modelo conozca el principio de Metz: simplemente carece de memoria persistente entre archivos a lo largo de peticiones sucesivas. No sabe que en una sesión anterior ya escribió algo parecido, a menos que le introduzcas el código relacionado dentro de la ventana de contexto. El resultado es que en el producto de la IA conviven grandes bloques de código que «pintan para ser abstraídos pero no lo están» y otros que «están abstraídos pero en una dirección totalmente equivocada». Ambos tipos de error concentrados en el mismo repositorio: quizá esa sea la nueva normalidad que la programación asistida por IA reserva a quienes mantienen el código.
Elegir entre dos errores
La postura de Metz se simplifica a menudo como «copiar es mejor que abstraer», lo cual no es del todo justo. Lo que realmente dice es: si te ves obligado a elegir entre duplicación y una abstracción equivocada, elige la duplicación. Es un principio de segundo orden: no te dice qué es lo correcto, sino que, cuando no estás seguro de qué es lo correcto, te indica en qué dirección el error resulta más barato.
Un comentario muy votado de HN ofreció una regla práctica: la «regla de tres». La primera vez que algo aparece, lo escribes. La segunda, toleras la duplicación pero empiezas a observar. La tercera, consideras abstraer, y solo lo haces en el eje que realmente varía. Esta regla encierra una premisa clave: necesitas tiempo para que el patrón verdadero emerja. Solo después de que el código lleve un tiempo rodando en el repositorio se vuelve distinguible qué puntos de llamada van a cambiar al unísono y cuáles van a separarse.
Otro comentarista lo resumió con más filo aún: «Lo contrario de DRY no es la duplicación; es WET: Write Everything Twice. Escribe todo dos veces, luego observa. A la tercera, muévete.»
Juicio de ingeniería después de los datos
Las cifras de HN –409 puntos, 272 comentarios– indican que este asunto toca una fisura todavía no cerrada dentro de la comunidad de ingenieros. Todo el mundo sabe que DRY se puede aplicar mal. El problema es que, generación tras generación, los nuevos ingenieros siguen recibiendo una formación que sitúa «eliminar duplicación» como una prioridad no discutible en la revisión de código.
En una época en la que la IA puede escribir por ti código correcto, la capacidad genuinamente escasa ya no es «cómo abstraer», sino «cuándo abstraer». Esta última no exige destreza, sino paciencia, juicio formado tras una observación prolongada del dominio de negocio, y la sangre fría necesaria para desmontar una abstracción frente al coste hundido. Las palabras originales de Metz resuenan hasta hoy: «Cuando te enfrentas a una abstracción equivocada, la forma más rápida de avanzar es retroceder.»
No hay una respuesta definitiva en este asunto. El autor no mantiene una postura absoluta a favor de ninguno de los dos bandos. La abstracción es uno de los pocos conceptos verdaderamente fundacionales de la ingeniería de software, pero su valor depende críticamente del momento y del contexto. Este artículo no propugna sustituir la abstracción por la copia. Lo que señala es un juicio más acotado: en la nueva normalidad donde humanos y máquinas producen código por turnos, el coste de «esperar un poco más antes de abstraer» quizá sea más bajo de lo que hemos creído durante mucho tiempo, y el coste de «desmontar después de haber abstraído mal» quizá sea más alto de lo que habíamos previsto.
Declaración del autor: este artículo se basa en la lectura y análisis del texto original de Sandi Metz, del hilo de HN de junio de 2026 y de literatura de ingeniería relacionada. No constituye asesoramiento técnico absoluto. Las decisiones de ingeniería deben tomarse en función del contexto concreto: tamaño del equipo, fase del negocio, antigüedad del repositorio, cobertura de tests… Cualquiera de estas variables, por sí sola, puede dar la vuelta al sentido del juicio aquí expuesto.