Principio KISS — Keep It Simple
"Simplicity is the ultimate sophistication." — Leonardo da Vinci
"Make everything as simple as possible, but not simpler." — Albert Einstein
KISS (Keep It Simple, Stupid — o en su versión más amable, Keep It Simple and Straightforward) es el principio que dice que la mayoría de sistemas funcionan mejor si se mantienen simples en lugar de complejos. La complejidad debe justificarse, no acumularse por defecto.
El nombre puede sonar informal, pero el principio es uno de los más difíciles de aplicar. Escribir algo simple requiere un entendimiento profundo del problema. Escribir algo complejo es fácil — cualquier principiante puede hacerlo.
Qué es la complejidad
Antes de evitarla hay que entender de dónde viene. Hay dos tipos:
Complejidad esencial: la que viene del dominio del problema. Un sistema de pagos con múltiples monedas, impuestos por país y tipos de cambio variables es inherentemente complejo. Esa complejidad no se puede eliminar — es la naturaleza del problema.
Complejidad accidental: la que introducen las decisiones de diseño e implementación. Elegir una arquitectura de 12 microservicios para una aplicación que cabe en un monolito, usar un framework de 80MB para servir 3 endpoints, o añadir una capa de abstracción que nadie usa todavía — eso es complejidad accidental. Se puede y se debe evitar.
KISS se enfoca en minimizar la complejidad accidental. La complejidad esencial no es negociable, pero sí cómo la manejamos.
❌ Violaciones de KISS
Ejemplo 1 — Abstracción prematura innecesaria
# Queremos saludar al usuario
class GreetingStrategyFactory:
def create(greeting_type):
if greeting_type == "formal":
return FormalGreetingStrategy()
return InformalGreetingStrategy()
class FormalGreetingStrategy:
def greet(name): return "Good morning, " + name
class InformalGreetingStrategy:
def greet(name): return "Hey, " + name
# Uso
factory = GreetingStrategyFactory()
strategy = factory.create("formal")
result = strategy.greet("Duban")
Son 4 clases y un factory para lo que podría ser:
function greet(name, formal=False):
if formal:
return "Good morning, " + name
return "Hey, " + name
La abstracción compleja solo se justifica si hay múltiples tipos de saludo con lógica no trivial, si se añaden dinámicamente en tiempo de ejecución, o si necesitas inyectar la estrategia para tests. Si ninguna de esas condiciones se cumple, es over-engineering.
Ejemplo 2 — Solución compleja a un problema simple
# Necesitamos el máximo de dos números
function get_maximum(a, b):
numbers = [a, b]
numbers.sort(reverse=True)
return numbers[0]
El problema: sort es O(n log n). Para dos elementos. La solución simple:
function get_maximum(a, b):
return a if a > b else b
Un lector entiende la segunda versión inmediatamente. La primera requiere rastrear la lógica.
Ejemplo 3 — Arquitectura sobredimensionada para el problema
Un sistema de gestión de tareas personal que usa:
- Kafka para mensajería entre sus 8 microservicios
- 3 bases de datos distintas (PostgreSQL, Redis, Elasticsearch)
- Un API Gateway con circuit breaker
- Service mesh con Istio
...cuando el sistema va a tener 50 usuarios y cabe perfectamente en un monolito con una sola base de datos y caché simple.
La complejidad accidental aquí es enorme: costes operativos, debugging distribuido, latencia de red entre servicios, consistencia eventual que el dominio no requiere.
✅ Aplicación de KISS
Principio: resuelve el problema actual, no el imaginado
# ❌ Diseñado para el futuro imaginado
class NotificationService:
def send(notification):
strategy = self.strategy_registry.get(notification.channel)
formatter = self.formatter_factory.create(notification.template)
router = self.channel_router.resolve(notification.priority)
...
# ✅ Diseñado para lo que se necesita hoy
function send_notification(user, message):
email_client.send(to=user.email, body=message)
Si mañana necesitas SMS además de email, añades ese caso. No lo construyes antes de necesitarlo. Ver también .
Principio: prefiere lo conocido sobre lo ingenioso
# ❌ Ingenioso pero ilegible
result = [x for x in range(1, n+1) if not any(x % i == 0 for i in range(2, x))]
# ✅ Simple y claro
primes = []
for num in range(2, n+1):
is_prime = True
for divisor in range(2, num):
if num % divisor == 0:
is_prime = False
break
if is_prime:
primes.append(num)
El código ingenioso impresiona en entrevistas. El código simple salva tiempo en producción.
Principio: nombra con intención, no con brevedad
# ❌ Breve pero opaco
def calc(x, y, z):
return x * y * (1 - z)
# ✅ Claro y autoexplicativo
def calculate_discounted_price(unit_price, quantity, discount_rate):
return unit_price * quantity * (1 - discount_rate)
El código se lee muchas más veces de las que se escribe. La legibilidad es una forma de simplicidad.
YAGNI — You Aren't Gonna Need It
YAGNI es el corolario práctico de KISS aplicado a funcionalidades:
"Always implement things when you actually need them, never when you just foresee that you need them." — Ron Jeffries, XP
No implementes lo que no necesitas ahora. Añadir una funcionalidad "por si acaso la necesitamos en el futuro" tiene un coste real:
- Tiempo de desarrollo hoy que podría ir a funcionalidades que sí se necesitan.
- Mantenimiento de código que nadie usa.
- Complejidad que hace más difícil entender el sistema.
- Riesgo de que cuando la necesites de verdad, el diseño "especulativo" no sea el correcto.
El código más simple que puede funcionar es el que no existe todavía.
❌ Violación de YAGNI
# El requisito era: mostrar el perfil del usuario
# Lo que se implementó:
class UserProfileService:
def get_profile(user_id): ...
def get_profile_with_privacy_settings(user_id): ... # nadie lo pidió
def get_public_profile(user_id): ... # nadie lo pidió
def export_profile_to_pdf(user_id): ... # nadie lo pidió
def get_profile_history(user_id, since, until): ... # nadie lo pidió
✅ Aplicación de YAGNI
class UserProfileService:
def get_profile(user_id): ... # esto es lo que se necesita ahora
Si mañana alguien necesita la exportación a PDF, se añade. Mientras tanto, no existe.
KISS y su relación con SOLID y DRY
Los tres principios forman un triángulo de tensión que hay que calibrar:
SOLID puede requerir más capas (abstracciones, interfaces, inyección de dependencias). KISS pone freno: no apliques OCP ni DIP si no hay razón concreta para hacerlo.
DRY puede llevar a abstracciones forzadas que unifican cosas que no deberían estarlo. KISS pone freno: si la abstracción DRY hace el código más difícil de entender, la duplicación controlada es mejor.
La calibración en Wiedii:
- Código desechable o de un solo uso: KISS domina.
- Código de dominio que cambiará: SOLID domina para hacerlo extensible.
- Código compartido entre módulos o servicios: DRY domina para evitar inconsistencias.
Cómo medir si algo es "suficientemente simple"
Tres preguntas prácticas:
¿Un compañero puede entender esto sin preguntar? Si necesitas una explicación oral de cómo funciona el código, probablemente es más complejo de lo necesario.
¿Cuánto tiempo tardarías en debuggearlo a las 2am? La complejidad real se revela en producción, con presión y sin contexto.
¿Cuántas piezas móviles tiene y cuántas son necesarias para el requisito actual? Cada abstracción, capa, servicio o patrón que no responde a un requisito presente es complejidad accidental.
Señales de alerta de violación KISS
- Código que nadie del equipo puede explicar sin consultar la documentación.
- "Arquitectura de anticipación" — diseño para requisitos que no existen.
- Patrones de diseño aplicados donde no hay un problema que justifique su complejidad.
- Herramientas o frameworks elegidos por su potencial en lugar de por la necesidad actual.
- El tiempo de onboarding de un nuevo desarrollador aumenta significativamente porque "el sistema es complejo".
- Comentarios que explican qué hace el código en lugar de por qué (señal de que el qué no es obvio — debería serlo).
En Wiedii
La complejidad accidental es el mayor enemigo de la velocidad de desarrollo sostenible. Un sistema más simple:
- Se onboardea más rápido (nuevo dev productivo antes).
- Se debugea más rápido (menos piezas móviles que inspeccionar).
- Se testea más rápido (menos mocking, menos setup).
- Se despliega más confiado (menos superficie de fallo).
La heurística práctica de Wiedii: si no puedes explicar el diseño en dos frases a un compañero, probablemente es más complejo de lo que necesita ser. Busca la versión más simple que resuelva el problema real, no el imaginado.
Documentos relacionados
- SOLID — Cuando la complejidad adicional de SOLID sí está justificada
- DRY — DRY puede crear abstracciones complejas; KISS las pone en perspectiva
- politicas-core — Políticas de desarrollo Wiedii