Empiezas con una promoción del 10 % y, un rato después, estás diseñando un motor configurable con prioridades, extensiones y soporte para campañas que todavía no existen. El descuento sigue sin funcionar, pero el futuro está preparadísimo.
KISS, DRY y YAGNI ayudan a revisar decisiones como esta. Son principios sencillos de enunciar y bastante interesantes cuando los llevamos a un caso real.
KISS: cuánto cuesta entender la solución
KISS, habitualmente desarrollado como Keep It Simple, Stupid, invita a mantener las soluciones simples. La parte interesante está en decidir qué entendemos por simple.
Una función de una línea puede esconder una expresión imposible de leer. Una solución dividida en veinte métodos puede obligarnos a saltar entre archivos para seguir un cálculo elemental. Conviene mirar cuánto cuesta entender el recorrido completo.
Pensemos en una regla pequeña: el envío cuesta 5 euros y es gratuito a partir de 50 euros de compra. Podemos recibir el importe, validar que tenga sentido y comprobar ese límite. Un motor de reglas podría servir si aparecen condiciones que lo justifiquen; para esta necesidad debe aportar algo que compense su coste.
La simplicidad depende del problema y del equipo. Una construcción muy ingeniosa que solo entiende quien la escribió puede resultar cara de mantener. Unos nombres claros y un flujo previsible suelen ayudar más de lo que parece.
Para aplicar KISS: recorre la solución como si acabaras de llegar al proyecto. ¿Qué necesitas aprender para hacer un cambio pequeño? ¿Qué piezas podrías retirar sin perder comportamiento necesario?
Una regla que se puede seguir
Trabajamos en céntimos de euro: 5000 son 50 euros y 500 son 5. La clase solo calcula el coste del envío para el importe que recibe:
final class ShippingCost {
int calculate(int purchaseInCents) {
if (purchaseInCents < 0) {
throw new IllegalArgumentException("Purchase amount must not be negative.");
}
if (purchaseInCents >= 5000) {
return 0;
}
return 500;
}
}
Con ShippingCost shipping = new ShippingCost(), la llamada shipping.calculate(3000) devuelve 500: la compra de 30 euros paga 5 de envío. shipping.calculate(5000) devuelve 0, porque exactamente 50 euros ya cumple la condición. Un importe negativo produce un error antes de calcular nada. Para esta regla, una compra de cero euros también tiene un envío de 5; decidir si se admite una compra vacía corresponde al proceso de compra.
El recorrido tiene tres decisiones visibles: rechazar una entrada inválida, reconocer el envío gratuito y devolver el coste habitual. Introducir un contrato con varias implementaciones podría servir más adelante si hay transportistas o modalidades diferentes. Ahora podemos entender la regla sin aprender esa estructura.
Usamos enteros porque la operación solo compara importes y devuelve cantidades exactas en céntimos. Si añadimos porcentajes, tendremos que acordar cómo redondearlos; esa necesidad todavía no aparece aquí.
DRY: dónde vive cada regla
DRY, Don't Repeat Yourself, propone una representación única y autorizada de cada conocimiento dentro del sistema.
Si la política de descuentos aparece en el carrito y en la factura, una modificación puede dejar ambas versiones en desacuerdo. Tenemos dos sitios que deben recordar exactamente la misma regla.
Compartir ese cálculo permite que sus consumidores utilicen una referencia común. Antes de extraerlo, merece la pena comprobar que representan el mismo conocimiento.
Dos fragmentos parecidos pueden tener futuros distintos
Imagina un descuento del 10 % para empleados y otro del 10 % para una campaña. Hoy la operación matemática coincide. Mañana la campaña puede tener un límite de importe y el descuento de empleados puede cambiar según su antigüedad.
Si los unimos en una única política, pronto podríamos acabar con una llamada como esta. Es un contraejemplo ilustrativo: las banderas obligan a conocer combinaciones que la firma del método apenas explica.
calculateDiscount(purchase, isEmployee, isCampaign, hasLimit, specialMode);
La función compartida empieza a reunir casos que tienen razones diferentes para cambiar. Podemos compartir una operación matemática sin convertir ambas promociones en una sola política, siempre que coincidan también sus unidades y su criterio de redondeo.

Las flechas significan «utiliza». Cada política decide si corresponde aplicar su descuento y con qué porcentaje; ambas pueden utilizar una operación común para el cálculo matemático. El esquema separa esa operación de las condiciones que deciden si procede usarla. Si mañana cambia la elegibilidad de empleados, la campaña puede seguir igual. Antes de compartir una función conviene identificar precisamente ese límite.
El ejemplo del envío permite verlo con otra regla: si el carrito y el resumen aplican exactamente el mismo límite de 50 euros, ambos pueden consultar ShippingCost. Si una tarifa de recogida tiene otras condiciones, su parecido inicial no basta para unirlas.
Para aplicar DRY: pregunta qué conocimiento se repite y si un cambio debería afectar a todos sus usos. El parecido visual del código es una pista; hace falta entender su significado.
YAGNI: cuánto futuro estamos construyendo
YAGNI, You Aren't Gonna Need It, cuestiona implementar capacidades que suponemos necesarias en el futuro.
Puede que un día nuestra tienda necesite campañas combinables, prioridades configurables y distintos proveedores de promociones. Cada posibilidad añade código que habrá que entender, probar y mantener desde ahora.
Además, el futuro tiene la costumbre de llegar con requisitos distintos de los que habíamos imaginado. El sistema de extensiones que preparamos quizá no encaje con la necesidad real.
Una forma de aplicar el principio consiste en imaginar el cambio posterior. Si añadir una segunda política dentro de unos meses sería sencillo, quizá podamos esperar a conocer sus condiciones antes de diseñar cómo se incorpora.
Las necesidades actuales incluyen las obligaciones conocidas de seguridad, integridad y funcionamiento. También puede ser necesario investigar una decisión costosa antes de comprometerse con ella. Conviene distinguir esas necesidades de una posibilidad que hemos imaginado tomando café.
Esperar resulta más viable cuando el código se deja cambiar. Mantener pruebas del comportamiento actual y refactorizar el código, por ejemplo mejorando nombres o reorganizando una función difícil de entender, ayuda a incorporar requisitos cuando lleguen. Ese trabajo cuida una necesidad presente: poder evolucionar la aplicación. Si posponemos también ese cuidado, cada cambio futuro puede salir más caro.
Para aplicar YAGNI: identifica qué requisito presente justifica cada capacidad y qué información falta para diseñarla bien.
Podemos comprobarlo con una petición concreta: «El descuento de bienvenida solo se aplica a la primera compra». Necesitamos decidir cómo conocer ese dato y probar la condición. El soporte para campañas por geolocalización puede esperar si nadie lo ha pedido. Esa diferencia nos ayuda a invertir el esfuerzo en el comportamiento que vamos a entregar.
Cómo trabajan juntos
Volvamos a la promoción del 10 %. Aclaramos sus condiciones y escribimos una solución fácil de seguir. Si el carrito y la factura necesitan la misma regla, la dejamos en un lugar común. El soporte para promociones todavía desconocidas puede esperar.
Con el tiempo aparece una campaña diferente. Ahora tenemos dos casos reales que comparar: qué comparten, qué cambia y qué contrato de colaboración podría servirles. Esa información permite elegir una abstracción con más criterio.
Los tres principios dejan preguntas útiles para revisar el diseño:
- KISS: ¿se entiende la solución con un esfuerzo razonable?
- DRY: ¿tenemos una referencia clara para el conocimiento que debe mantenerse coherente?
- YAGNI: ¿esta capacidad responde a una necesidad que conocemos?
Cuando una decisión parece complicarse, volver a estas preguntas suele ser un buen comienzo. Si para aplicar un descuento necesitamos explicar media plataforma, quizá el descuento esté pidiendo una solución más pequeña.