Principio DRY en JavaScript: Don't Repeat Yourself
Aprende a aplicar el principio DRY para eliminar duplicación de código y crear aplicaciones más mantenibles y escalables.
TL;DR - Resumen rápido
- El principio DRY significa Don't Repeat Yourself: cada pieza de conocimiento debe tener una única representación
- La duplicación de código causa problemas de mantenimiento, bugs y inconsistencias
- Extrae código duplicado en funciones reutilizables con parámetros apropiados
- Usa abstracción y patrones de diseño para eliminar duplicación estructural
- Aplica DRY con criterio: no sacrifies legibilidad por evitar duplicación trivial
Introducción al Principio DRY
El principio DRY (Don't Repeat Yourself) es uno de los fundamentos más importantes de la programación moderna. Enunciado por Andy Hunt y Dave Thomas en The Pragmatic Programmer, establece que cada pieza de conocimiento debe tener una única, inequívoca y autorizada representación dentro de un sistema. En términos prácticos, esto significa evitar la duplicación de código siempre que sea posible.
La duplicación de código es más que un problema estético: es un problema técnico serio que afecta la mantenibilidad, la calidad y la evolución del software. Cuando el mismo código existe en múltiples lugares, cualquier cambio requiere modificaciones en todas esas ubicaciones, aumentando la probabilidad de errores y haciendo el mantenimiento más costoso y propenso a fallos.
- Reduce el mantenimiento: los cambios se hacen en un solo lugar
- Disminuye la probabilidad de bugs: menos código que mantener
- Mejora la consistencia: el mismo comportamiento en todo el sistema
- Facilita el testing: menos código que probar
- Aumenta la reutilización: el código está diseñado para ser compartido
¿Qué es el Principio DRY?
El principio DRY establece que cualquier lógica o funcionalidad debe existir en un solo lugar del código. Cuando necesitas esa funcionalidad, la invocas desde ese lugar único en lugar de duplicarla. Esto no significa que no puedas tener código que se parezca visualmente, sino que no debes tener código que implemente la misma lógica o regla de negocio en múltiples lugares.
Tipos de Duplicación
La duplicación puede manifestarse de varias formas: duplicación de código literal, duplicación de lógica con diferente implementación, y duplicación estructural. Cada tipo requiere un enfoque diferente para eliminarla, pero todas violan el principio DRY y deben abordarse sistemáticamente.
El ejemplo muestra tres tipos de duplicación: duplicación literal (el mismo código copiado), duplicación de lógica (misma funcionalidad implementada de forma diferente) y duplicación estructural (el mismo patrón repetido). Cada una de estas debe ser extraída a una función reutilizable que capture la lógica común.
Es importante distinguir duplicación de similitud. No toda similitud es duplicación real: si dos piezas de código se parecen pero sirven propósitos diferentes y evolucionarán independientemente, no necesariamente deben unificarse. DRY se aplica cuando la misma lógica de negocio o funcionalidad se repite en múltiples contextos.
Detectar Duplicación de Código
El primer paso para aplicar DRY es identificar dónde existe la duplicación. Las herramientas de análisis estático de código, el code review y la refactorización continua son prácticas efectivas para detectar duplicación. Los patrones comunes incluyen funciones que hacen casi lo mismo, bloques de código copiados con pequeños cambios, y validaciones repetidas en múltiples lugares.
Patrones Comunes de Duplicación
Hay patrones de duplicación que aparecen frecuentemente en código JavaScript. Reconocer estos patrones te ayudará a identificar oportunidades para aplicar DRY de manera proactiva. Los patrones más comunes incluyen validaciones repetidas, formateo duplicado, y lógica de negocio replicada.
Los ejemplos muestran patrones comunes de duplicación: validación de email en múltiples lugares, formateo de moneda duplicado, y cálculo de precio con impuesto repetido. Cada uno de estos patrones debe extraerse a una función reutilizable que centralice la lógica.
- <strong>Validaciones repetidas:</strong> La misma validación en múltiples funciones
- <strong>Formateo duplicado:</strong> El mismo formateo de datos en diferentes lugares
- <strong>Cálculos repetidos:</strong> La misma fórmula matemática en múltiples contextos
- <strong>Transformaciones duplicadas:</strong> La misma conversión de datos en varios módulos
Herramientas de Detección
Las herramientas automatizadas pueden ayudarte a detectar duplicación de código de manera sistemática. ESLint con plugins específicos, herramientas de análisis de código como SonarQube, y características de los IDEs modernos pueden identificar duplicación que podría pasar desapercibida durante el code review manual.
Las reglas de ESLint como no-duplicate-imports detectan importaciones duplicadas, mientras que herramientas más avanzadas pueden detectar duplicación de lógica y código. Configurar estas herramientas en tu proyecto ayuda a mantener el principio DRY de manera preventiva, alertándote cuando introduces duplicación involuntaria.
Detección continua
Integra la detección de duplicación en tu pipeline de CI/CD. Herramientas como SonarQube pueden bloquear pull requests que introducen demasiada duplicación, asegurando que el principio DRY se mantenga de manera consistente en todo el equipo.
Extracción de Funciones
La extracción de funciones es la técnica más directa para aplicar DRY. Cuando identificas código duplicado, extrae la lógica común a una función con un nombre descriptivo. Esta función debe recibir parámetros para las partes que varían y devolver el resultado. La clave es que la función capture la intención y la lógica, no solo el código literal.
Extracción Básica
La extracción básica consiste en identificar bloques de código que se repiten y moverlos a una función separada. La función debe tener un nombre que describa claramente qué hace, y recibir como parámetros los valores que varían entre las diferentes instancias del código duplicado.
El ejemplo muestra cómo extraer validación duplicada a una función reutilizable. En lugar de repetir la misma lógica de validación en múltiples lugares, creamos una función `validarEmail` que encapsula la lógica. Ahora cualquier cambio en la validación de email se hace en un solo lugar.
Extracción de Lógica Compleja
Para lógica más compleja, la extracción puede requerir dividir el código en múltiples funciones más pequeñas, cada una con una responsabilidad específica. Esto no solo elimina duplicación, sino que también mejora la legibilidad y testabilidad del código al descomponer operaciones complejas en pasos más simples.
El ejemplo muestra cómo extraer lógica compleja de cálculo de precio en funciones más pequeñas y reutilizables. La función principal `calcularPrecioFinal` compone las funciones auxiliares para calcular el precio con impuestos y descuentos. Cada función auxiliar puede reutilizarse independientemente.
Composición sobre herencia
Al extraer funciones complejas, prefiere la composición de funciones pequeñas sobre crear funciones monolíticas. Las funciones pequeñas son más fáciles de entender, probar y reutilizar en diferentes contextos.
Parametrización
La parametrización es la técnica de convertir valores fijos en parámetros para hacer el código más reutilizable. Cuando tienes código que hace casi lo mismo pero con diferentes valores, esos valores deben convertirse en parámetros de una función. Esto elimina la duplicación mientras mantiene la flexibilidad para manejar diferentes casos.
Parámetros de Configuración
Los parámetros de configuración permiten que una misma función se comporte de diferentes maneras según los valores que recibe. Esto es especialmente útil para validaciones, formateo y operaciones que tienen variaciones predecibles pero consistentes. La clave es identificar qué valores varían y convertirlos en parámetros.
El ejemplo muestra cómo parametrizar una función de validación para que sea más flexible. En lugar de tener múltiples funciones de validación para diferentes longitudes, creamos una función que recibe la longitud mínima como parámetro. Esto elimina la duplicación mientras mantiene la flexibilidad.
Parámetros Opcionales y Defaults
Los parámetros opcionales con valores por defecto permiten que una función maneje tanto casos simples como complejos sin duplicación. Los valores por defecto proporcionan comportamiento estándar, mientras que los parámetros opcionales permiten personalización cuando es necesario. Esto reduce la necesidad de múltiples funciones para variaciones similares.
El ejemplo muestra cómo usar parámetros opcionales con valores por defecto para crear una función flexible. La función `formatearFecha` tiene un valor por defecto para el locale, pero permite personalizarlo cuando es necesario. Esto evita tener múltiples funciones de formateo para diferentes locales.
Advertencia sobre demasiados parámetros
Evita funciones con demasiados parámetros (más de 4-5). Si necesitas muchos parámetros, considera usar un objeto de configuración que agrupe los parámetros relacionados. Esto mejora la legibilidad y hace más fácil agregar o quitar parámetros sin romper el código existente.
Abstracción y Patrones
Cuando la duplicación es estructural o patrones de diseño se repiten, la extracción simple de funciones no es suficiente. En estos casos, necesitas abstracción: crear estructuras de nivel superior que capturen el patrón común y permitan variaciones específicas. Los patrones de diseño como Strategy, Factory y Template Method son ejemplos de abstracción para eliminar duplicación.
Patrones de Diseño para DRY
Los patrones de diseño proporcionan soluciones probadas para problemas recurrentes de duplicación. El patrón Strategy permite encapsular algoritmos intercambiables, Factory centraliza la creación de objetos, y Template Method define el esqueleto de un algoritmo. Estos patrones eliminan duplicación estructural mientras mantienen la flexibilidad.
El ejemplo muestra el patrón Strategy para eliminar duplicación en algoritmos de ordenamiento. En lugar de tener múltiples funciones de ordenamiento con lógica duplicada, encapsulamos cada algoritmo en un objeto strategy y tenemos una función que delega en la estrategia apropiada. Esto elimina duplicación estructural.
Abstracción apropiada
La abstracción debe ser apropiada al nivel de complejidad del problema. No sobre-abstraigas código simple, pero tampoco evites abstracción cuando el patrón es claro y repetitivo. El objetivo es eliminar duplicación significativa sin introducir complejidad innecesaria.
Composición de Funciones
La composición de funciones es una técnica poderosa para eliminar duplicación mediante la combinación de funciones pequeñas y reutilizables. En lugar de duplicar lógica compleja, la construyes componiendo funciones simples que cada una hace una cosa bien. Esto crea código más modular, testeable y fácil de entender.
El ejemplo muestra cómo componer funciones para procesar datos sin duplicación. Las funciones `validar`, `transformar` y `formatear` son pequeñas y reutilizables. La función `procesarUsuario` las compone para crear un flujo completo de procesamiento. Cada función puede reutilizarse independientemente en otros contextos.
Cuándo NO Aplicar DRY
Aunque DRY es un principio importante, no debe aplicarse ciegamente. Hay situaciones donde la duplicación es aceptable o incluso preferible. Aplicar DRY indiscriminadamente puede llevar a código sobre-abstracto, difícil de entender y mantener. El criterio es clave: evalúa si la duplicación es significativa y si la abstracción mejora o empeora el código.
Duplicación Aceptable
Hay casos donde la duplicación es aceptable: cuando el código es trivial y la abstracción añadiría complejidad, cuando las dos instancias evolucionarán independientemente, o cuando la duplicación es accidental y temporal. En estos casos, aplicar DRY puede ser una sobre-optimización que no aporta valor real.
El ejemplo muestra casos donde la duplicación es aceptable: código trivial como inicializar objetos, configuraciones que evolucionarán independientemente, y código temporal. En estos casos, la abstracción añadiría más complejidad que valor, y es preferible mantener la duplicación simple.
Advertencia sobre sobre-abstracción
La sobre-abstracción es tan peligrosa como la duplicación. Si tu abstracción es más compleja de entender que el código duplicado que reemplaza, probablemente has ido demasiado lejos. La regla de oro: la abstracción debe simplificar, no complicar.
El Principio WET
WET (Write Everything Twice) es el antónimo humorístico de DRY. Sugiere que está bien escribir código dos veces antes de abstraerlo. La idea es que la primera vez es para entender el problema, la segunda para identificar el patrón, y solo entonces abstraer. Esto evita abstracciones prematuras basadas en una sola instancia.
El ejemplo muestra el enfoque WET: primero escribes el código dos veces para entender el patrón, luego extraes la abstracción. Esto evita crear abstracciones basadas en una sola instancia que pueden no ser apropiadas cuando aparece una segunda variación.
La "Rule of Three" complementa este enfoque: debes duplicar código hasta tres veces antes de abstraerlo. La primera vez es accidental, la segunda es coincidencia, y la tercera es un patrón que merece abstracción. Esta regla te protege de abstracciones prematuras que complican el código sin aportar valor real.
Resumen: Principio DRY
Conceptos principales:
- •DRY significa Don't Repeat Yourself: cada conocimiento debe tener una única representación
- •La duplicación causa problemas de mantenimiento, bugs e inconsistencias
- •Tipos de duplicación: literal, lógica y estructural
- •Extracción de funciones y parametrización eliminan duplicación básica
- •Abstracción y patrones de diseño eliminan duplicación compleja
Mejores prácticas:
- •Identifica duplicación mediante code review y herramientas de análisis
- •Extrae código duplicado a funciones con nombres descriptivos
- •Usa parámetros para hacer funciones más reutilizables
- •Aplica patrones de diseño para duplicación estructural
- •No sacrifiques legibilidad por DRY: usa el criterio apropiado