DRY (Don't Repeat Yourself) es un principio fundamental de desarrollo formulado por Andy Hunt y Dave Thomas en el libro “The Pragmatic Programmer.” Establece que cada parte del conocimiento en un sistema debe tener una representación única, inequívoca y autoritativa. Según The Pragmatic Programmer, 20th Anniversary Edition, violar DRY implica que cambiar un elemento requiere ediciones en decenas de lugares, y cada fragmento omitido se convierte en una fuente de errores.
Puntos clave
DRY (Don't Repeat Yourself) es un principio de desarrollo que requiere almacenar cada elemento de conocimiento en un proyecto exactamente una vez. Esto significa que cualquier lógica, configuración o metadato debe existir en un solo lugar.
El término fue introducido por Andy Hunt y Dave Thomas en 1999 en el libro “The Pragmatic Programmer.” Los autores definieron DRY como “cada parte del conocimiento debe tener una representación única, inequívoca y autoritativa dentro del sistema.” Lo opuesto a DRY es el enfoque WET (Write Everything Twice), donde la duplicación se considera normal.
Según un estudio de la University of California, Davis (2019), los proyectos con altos niveles de duplicación de código gastan un 42% más de tiempo corrigiendo errores. La razón es que los desarrolladores deben encontrar y cambiar todas las copias del mismo fragmento, y la búsqueda manual inevitablemente lleva a omisiones.
Aplica DRY como criterio de calidad de código. Si notas que el mismo patrón aparece tres veces en un proyecto, extráelo en una abstracción sin esperar a una cuarta repetición.
Single Responsibility Principle (SRP) de SOLID establece que una clase debe tener una sola razón para cambiar. DRY es más amplio: abarca no solo clases, sino también datos, configuración, documentación e incluso reglas de negocio. SRP trata sobre los límites de responsabilidad; DRY trata sobre evitar copias.
En el desarrollo móvil, esta distinción es especialmente notoria. Si la misma regla de negocio (cálculo de impuestos, formato de fecha) se repite en las partes Android e iOS del proyecto, eso es una violación de DRY, aunque SRP se cumpla formalmente dentro de cada plataforma. La solución es extraer la lógica común en un módulo compartido (KMM, C++).
Según el informe Google Android Architecture Guidelines (2023), los equipos que utilizan módulos compartidos para la lógica de negocio reducen la cantidad de errores al cambiar requisitos en un 37% en comparación con proyectos que duplican la lógica entre plataformas.
La duplicación es la principal fuente de deuda técnica en proyectos móviles. Cada copia de código crea una dependencia oculta: para cambiar el comportamiento, debes encontrar y actualizar todas las copias. Omitir aunque sea una significa un error.
Considera un escenario clásico: en una aplicación Android, el formato de fecha se realiza en tres Activities diferentes. Al cambiar a un nuevo formato (por ejemplo, ISO 8601), el desarrollador corrige dos archivos, olvida el tercero y el usuario ve las fechas en el formato antiguo. La calificación de la aplicación baja y encontrar el error lleva el doble de tiempo.
Un estudio de Google Research (2020) mostró que el 68% de los errores críticos en aplicaciones móviles están relacionados con cambios no sincronizados en código duplicado. Además, corregir dicho error en producción cuesta 4,5 veces más que si el código hubiera sido unificado desde el principio.
Utiliza analizadores estáticos (Detekt, SwiftLint) con reglas que detecten copy-paste. Configura CI para que las solicitudes de extracción con más de N líneas de duplicación no pasen la revisión sin justificación.
Un anti-patrón típico es copiar un adaptador de RecyclerView con modificaciones menores. En lugar de un adaptador universal con configuración, los desarrolladores crean una clase separada para cada pantalla. La refactorización extrayendo una clase base común reduce el código en un 30–50%.
// Duplicación: dos adaptadores separados
class UserAdapter {
fun bind(item: User) { /* ... */ }
}
class ProductAdapter {
fun bind(item: Product) { /* ... */ }
}
// Refactorización DRY: clase base común
abstract class BaseAdapter<T> {
abstract fun bind(item: T)
}
class UserAdapter : BaseAdapter<User>() { /* ... */ }
class ProductAdapter : BaseAdapter<Product>() { /* ... */ }
En el primer ejemplo, cada adaptador reimplementa el mecanismo bind desde cero. Al agregar nueva lógica (analítica, registro), habría que modificar cada archivo. Una clase base elimina esta duplicación: la lógica común vive en un lugar, la lógica específica en las subclases.
En proyectos iOS, la configuración de URLSession (encabezados, tiempos de espera, manejo de errores) a menudo se duplica. Cada servicio crea su propia sesión con configuraciones repetidas.
// Duplicación: cada servicio configura la sesión desde cero
class UserService {
let session = URLSession(configuration: {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return cfg
}())
}
// DRY: fábrica unificada de sesiones
struct NetworkConfig {
static var session: URLSession {
let cfg = URLSessionConfiguration.default
cfg.timeoutIntervalForRequest = 30
cfg.httpAdditionalHeaders = ["Authorization": "Bearer ..."]
return URLSession(configuration: cfg)
}
}
Extraer la configuración en un NetworkConfig unificado garantiza que todos los servicios utilicen los mismos encabezados y tiempos de espera. Un cambio en un lugar se aplica automáticamente a todas las solicitudes, lo que reduce el riesgo de errores al cambiar claves API o versiones de protocolo.
La herencia es una forma natural de eliminar la duplicación: la lógica común se traslada a una clase base y la lógica específica a las subclases. Sin embargo, en el desarrollo móvil, el abuso de la herencia crea jerarquías rígidas difíciles de mantener. La composición (inyección de dependencias) es una alternativa más flexible.
Un análisis de Google I/O 2023: Modern Android Architecture mostró que el 76% de los equipos de Google prefieren la composición sobre la herencia para eliminar la duplicación. En lugar de un BaseViewModel con una docena de métodos, se recomienda extraer clases UseCase separadas para cada operación de negocio e inyectarlas donde se necesiten.
Elige la composición en todos los casos excepto relaciones “es-un.” Si la clase A es una especialización de la clase B, la herencia es apropiada. Si A simplemente usa la funcionalidad de B, utiliza composición.
Las clases utilitarias (Extensiones, Helpers) son la forma más simple de evitar la duplicación. Candidatos típicos: formato de fechas, validación de email, conversión de unidades, trabajo con SharedPreferences/UserDefaults.
// DRY: función unificada de formato de fecha
fun Date.toDisplayFormat(): String {
val sdf = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault())
return sdf.format(this)
}
// Uso en cualquier lugar de la aplicación
textView.text = Date().toDisplayFormat()
La extensión Date.toDisplayFormat() se declara una vez y está disponible en todo el proyecto. Si el formato debe cambiar de “dd.MM.yyyy” a “yyyy-MM-dd”, la corrección está en un solo archivo, no en cada Activity o Fragment donde ocurre el formateo. Esto es la esencia de DRY.
Los proyectos Android multimódulo a menudo duplican las versiones de dependencias en cada build.gradle. La solución es un version catalog (libs.versions.toml) que centraliza todas las versiones en un solo archivo.
Según la Documentación para desarrolladores de Android (2024), migrar a un version catalog reduce los conflictos de dependencias en un 52% y acelera las compilaciones gracias a un punto único de edición.
Implementa un version catalog al inicio del proyecto o durante la primera reorganización de módulos. Si el proyecto ya contiene duplicación, dedica un día a la migración: se amortiguará en la próxima actualización de librerías.
La abstracción prematura es el error más común de los principiantes. Un desarrollador ve dos líneas de código similares e inmediatamente las extrae en una función común. Un mes después, los requisitos cambian y la función común se llena de parámetros y banderas, volviéndose más compleja que la duplicación original. La regla de tres protege exactamente contra esto: no abstraigas algo que ha aparecido solo una o dos veces.
Martin Fowler en su libro Refactoring (2019) recomienda: “La duplicación de código no siempre es mala. La duplicación de conocimiento es mala.” Si dos líneas coinciden casualmente pero expresan conceptos diferentes, eso no es duplicación, es coincidencia. La regla de tres ayuda a distinguir la coincidencia accidental de la duplicación sistemática.
Antes de abstraer, evalúa la semántica. Código copiado con el mismo significado: violación de DRY. Código con significado diferente pero sintaxis similar: coincidencia que no requiere abstracción.
La parametrización excesiva ocurre cuando una sola función intenta cubrir todos los escenarios posibles mediante banderas y parámetros booleanos. Dicho código viola SRP y se vuelve ilegible. Síntoma: si una función tiene más de dos parámetros booleanos, es un code smell de abstracción excesiva.
En lugar de una función con una bandera useCache: Boolean, es mejor crear dos funciones separadas con nombres claros: fetchFromNetwork() y fetchFromCache(). La claridad es más importante que una abstracción seca: esto coincide con el principio KISS.
Refactoriza la parametrización excesiva cuando una función alcance 3+ parámetros booleanos. Divide en funciones separadas con nombres claros: cada llamada se volverá autodocumentada.
Preguntas frecuentes
DRY (Don't Repeat Yourself) es un principio que requiere almacenar cada unidad lógica en un solo lugar. Si el mismo código aparece en múltiples partes de un proyecto, es una violación de DRY. Solución: extrae la lógica repetitiva en una función, clase o módulo separado.
WET (Write Everything Twice) es lo opuesto a DRY, donde la duplicación se considera aceptable. En proyectos WET, el mismo fragmento de código puede existir en cinco copias, y cuando los requisitos cambian, el desarrollador corrige cada copia por separado. WET aumenta el riesgo de errores y ralentiza el desarrollo.
DRY es perjudicial cuando lleva a una abstracción prematura: cuando dos secciones de código similares pero semánticamente diferentes se fusionan forzosamente en una función. Esto genera código complejo y sobrecargado de parámetros. La regla de tres ayuda a evitar este error: abstrae solo después de la tercera repetición.
En Android, DRY se aplica mediante version catalogs (libs.versions.toml), clases base comunes para adaptadores, fábricas de ViewModel y extensiones utilitarias de Kotlin. Se recomienda extraer la lógica de negocio en módulos compartidos (KMM) y usar View Binding para eliminar la duplicación de findViewById.
En iOS, DRY se logra mediante protocolos con implementación predeterminada, configuraciones de red compartidas (NetworkConfig), fábricas de celdas UICollectionView y paquetes SPM con lógica de negocio común. Las extensiones de tipos estándar (Date, String, URL) reducen la duplicación en formato y validación.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también