16. Comparación entre DRY, KISS y YAGNI: cómo se complementan

Los principios DRY, KISS y YAGNI comparten un objetivo: mantener el software simple, coherente y enfocado. En proyectos con Java, adoptarlos en conjunto permite evitar duplicaciones, reducir la complejidad accidental y posponer el trabajo especulativo.

Comprender cómo se complementan ayuda a tomar decisiones equilibradas. Cada principio aborda un ángulo distinto de la simplicidad, y su combinación protege al equipo de desviarse hacia soluciones innecesarias.

16.1 Propósitos individuales

  • DRY (Don't Repeat Yourself): eliminar duplicación de conocimiento para que la lógica viva en un único lugar.
  • KISS (Keep It Simple, Stupid): construir soluciones claras, directas y fáciles de entender.
  • YAGNI (You Ain't Gonna Need It): implementar solo lo que el negocio necesita ahora, posponiendo extensiones hipotéticas.

16.2 Cómo se potencian entre sí

Aplicar solo uno de los principios puede crear desequilibrios. Por ejemplo, eliminar duplicaciones sin cuidar la claridad produce abstracciones difíciles de leer; mantener el código simple pero repetido incrementa el costo de mantenimiento; evitar funcionalidades futuras sin DRY puede multiplicar inconsistencias. Al usar los tres:

  • DRY centraliza el conocimiento y facilita que KISS y YAGNI operen sobre una base coherente.
  • KISS asegura que las abstracciones creadas por DRY sean comprensibles.
  • YAGNI evita que se creen abstracciones innecesarias en nombre de DRY o KISS.

16.3 Matriz de decisiones

La siguiente tabla resume qué preguntar ante una nueva funcionalidad:

Pregunta Principio relacionado Acción recomendada
¿Existe código similar que ya resuelva esto? DRY Reutilizar o extraer la lógica compartida en un módulo único.
¿El diseño es comprensible para alguien que lo lee por primera vez? KISS Simplificar nombres, dividir responsabilidades y eliminar pasos superfluos.
¿Hay evidencia de que realmente se necesita esta funcionalidad ahora? YAGNI Posponer la implementación hasta contar con métricas o requerimientos claros.

16.4 Ejemplo en Java: combinando los tres principios

Repasemos un caso donde los tres principios se aplican en conjunto. Partimos de una implementación que intenta ser extensible desde el inicio, con duplicaciones y lógica dispersa.

// Implementación inicial: duplicación, complejidad y lógica especulativa
class NotificationService {
    private final EmailClient emailClient;
    private final SmsClient smsClient;
    private final FeatureToggle toggles;

    NotificationService(EmailClient emailClient,
                        SmsClient smsClient,
                        FeatureToggle toggles) {
        this.emailClient = emailClient;
        this.smsClient = smsClient;
        this.toggles = toggles;
    }

    void notifyOrderCreated(Order order) {
        if (toggles.isEnabled("EMAIL_NOTIFICATIONS")) {
            emailClient.send(order.customerEmail(), buildEmail(order));
        }
        if (toggles.isEnabled("SMS_NOTIFICATIONS")) {
            smsClient.send(order.customerPhone(), buildSms(order));
        }
        if (toggles.isEnabled("PUSH_NOTIFICATIONS")) {
            // Implementación pendiente para futuras plataformas
        }
    }

    private String buildEmail(Order order) {
        return "Pedido " + order.id() + " creado";
    }

    private String buildSms(Order order) {
        return "Pedido " + order.id() + " listo";
    }
}

La refactorización aplica DRY, KISS y YAGNI: se extrae la lógica común de mensajes (DRY), se simplifica el flujo (KISS) y se elimina la rama especulativa de push notifications (YAGNI).

// Implementación refinada: responsabilidades claras y sin suposiciones futuras
final class NotificationService {
    private final List<Notifier> notifiers;

    NotificationService(List<Notifier> notifiers) {
        this.notifiers = List.copyOf(notifiers);
    }

    void notifyOrderCreated(Order order) {
        String message = MessageTemplates.pedidoCreado(order);
        notifiers.forEach(notifier -> notifier.send(order, message));
    }
}

interface Notifier {
    void send(Order order, String message);
}

Las implementaciones concretas de Notifier se crean solo para canales existentes. Si mañana surge un canal adicional con evidencia real, se agregará una nueva clase sin modificar el servicio principal.

16.5 Prácticas para equilibrar los tres principios

  • Agregar preguntas específicas de DRY, KISS y YAGNI a las revisiones de código.
  • Registrar en la documentación técnica cuándo se aplicó cada principio y qué métricas lo respaldan.
  • Limitar el trabajo en progreso para detectar duplicaciones o complejidad mientras el contexto sigue fresco.
  • Adoptar métricas de calidad (duplicación, complejidad ciclomática, lead time) que reflejen el equilibrio entre los principios.

16.6 Checklist de uso combinado

  • ¿La nueva funcionalidad reutiliza la lógica existente o duplicó código? (DRY)
  • ¿El diseño se explica con pocas frases y tiene un flujo lineal? (KISS)
  • ¿Existe evidencia actual que justifique la incorporación? (YAGNI)
  • ¿Se definió cómo medir el impacto tras la entrega?
  • ¿El equipo acordó revisar la decisión si cambian los requisitos?

DRY, KISS y YAGNI no compiten: se refuerzan mutuamente. Mantenerlos en equilibrio permite entregar valor con rapidez, minimizar la complejidad y conservar la flexibilidad para responder a cambios del negocio.