Módulo 2: Cuándo NO abstraer
1. Presentación del módulo: el patrón que no pusiste
Descripción
Al terminar esta lección vas a entender por qué este módulo está exactamente donde está —antes de que la guía enseñe un solo patrón a fondo— y qué idea lo ordena por dentro. Vas a conocer en detalle el rincón sobre-diseñado de Boletia que vamos a desmontar en el proyecto: cuántos archivos son, cuántos saltos hay que dar para seguir su flujo, y cuánto código de verdad hace trabajo. Y vas a salir con una pregunta de bolsillo que sirve para el resto de tu carrera: "¿cuántas implementaciones distintas existen hoy?".
Esto importa por una razón incómoda. En el mercado, el daño más caro que se hace con patrones de diseño no es no usarlos. Es usarlos donde no hacían falta. Un sistema sin patrones es un sistema con condicionales largos y funciones grandes: es feo, se lee mal, y cualquiera que lo abra sabe inmediatamente qué está pasando y dónde tocar. Un sistema sobre-patronado es otra cosa: se ve profesional, tiene nombres elegantes, las clases son pequeñas, cada archivo cabe en una pantalla… y nadie puede responder qué pasa cuando el usuario aprieta un botón sin abrir cuatro archivos y perder el hilo dos veces. El primero se arregla en una tarde. El segundo lleva dos años sin que nadie se atreva a tocarlo.
Casi todos los cursos de patrones enseñan solo la primera dirección: aquí falta estructura, ponle un patrón. Este módulo enseña la segunda, que es la que separa a quien aplica patrones de quien decide sobre ellos. Y va antes a propósito. Si aprendes Strategy antes de aprender cuándo no usar Strategy, vas a salir de la lección con un martillo nuevo y el resto del código te va a parecer una caja de clavos. Poner el freno antes que el acelerador es, probablemente, la decisión de diseño más importante de esta guía. Sin este módulo, los cinco siguientes producirían sobre-ingeniería con nombres elegantes.
Conexión con el módulo: esta lección es el marco. Aquí todavía no vamos a desmontar nada; vamos a mirar el rincón que se va a desmontar y a instalar la idea que ordena las siete lecciones siguientes. La lección 2 pone precio a la abstracción: cuáles son exactamente sus cuatro costos y por qué "más flexible" es una compra y no un regalo. La lección 3 te da la heurística que evita la mayoría de los errores —la regla de tres— y explica por qué con dos casos todavía no sabes cuál es el eje de variación. La lección 4 compara de frente la duplicación con la abstracción equivocada, y llega a una conclusión que suena a herejía y no lo es: duplicar dos veces suele ser más barato que abstraer mal una vez. La lección 5 desarma YAGNI y su matiz, para que no lo confundas con "nunca pienses en el futuro". La lección 6 es el corazón del módulo: el anti-patrón del plugin para una sola implementación, con el código completo de Boletia sobre la mesa. La lección 7 te da el tradeoff de fondo —acoplamiento contra indirección— y los tres ejes con los que se decide. Y la lección 8, el proyecto, te pide desmontar el rincón y entregar la justificación.
El vuelo con tres escalas
Piensa en dos maneras de ir de Ciudad de México a Bogotá.
La primera es un vuelo directo. Subes, vuelas, bajas. Cuatro horas. Si algo sale mal, hay exactamente un lugar donde pudo salir mal.
La segunda es un itinerario con tres escalas: México → Panamá → Medellín → Bogotá. Alguien lo armó con un argumento perfectamente razonable: "así, si algún día quieres quedarte en Panamá o bajarte en Medellín, ya está la ruta puesta". Es cierto. La flexibilidad existe. Pero fíjate en lo que compraste con ella: tres despegues en vez de uno, tres aterrizajes, dos esperas en salas de tránsito, tres oportunidades de que la maleta se pierda, y un viaje de once horas. Y todo eso lo pagas cada vez que viajas, no solo el día que decidas bajarte en Medellín.
Ahora la pregunta que importa: ¿cuántas veces te bajaste en Medellín?
Si la respuesta es "muchas, es parte del negocio", la ruta con escalas se ganó su lugar. Si la respuesta es "nunca, en tres años", entonces llevas tres años pagando once horas para tener una opción que no usaste. Y lo peor no es el tiempo: es que ahora todo el equipo cree que ese itinerario es el correcto, y nadie propone el vuelo directo porque supone que hay una razón para las escalas.
Eso, casi literalmente, es la indirección en el código. Cada capa que agregas entre quien pide algo y quien lo hace es una escala: te da la opción de cambiar de avión, y te cobra un despegue y un aterrizaje en cada viaje. La opción puede valer muchísimo. Lo que nunca es cierto es que sea gratis.
Y aquí viene lo que hace distinto a este módulo. Casi toda la literatura de patrones te describe con lujo de detalle la escala en Panamá: qué tan cómoda es la sala, cómo se conecta con otros vuelos, todas las cosas que podrías hacer desde ahí. Casi ninguna te dice las once horas. Este módulo te dice las once horas.
Ejemplo trabajado: el rincón que nadie toca
Vamos a ver el caso concreto, porque el argumento abstracto es fácil de aceptar y fácil de olvidar. Este es el rincón de Boletia que vamos a desmontar, y lo vamos a mirar hoy sin arreglarlo todavía.
Recuerda el contexto de Boletia: es una plataforma mediana de venta de boletos para eventos. Cuando alguien compra un boleto para un evento con asientos numerados —un teatro, por ejemplo— el sistema tiene que decidir qué asiento le toca. Esa es toda la funcionalidad de la que estamos hablando: asignar un asiento libre a un boleto.
Hace dos años, alguien del equipo la implementó así:
boletia/plugins/
├── __init__.py # 4 líneas — dispara el descubrimiento al importar
├── base.py # 38 líneas — SeatingPlugin: la interfaz abstracta
├── registry.py # 61 líneas — registro, descubrimiento y resolución
├── config.py # 9 líneas — qué plugin usar para cada evento
└── impls/
└── default_seating.py # 71 líneas — la ÚNICA implementación que existe
Cinco archivos. Ciento ochenta y tres líneas. Un decorador de registro, un descubrimiento dinámico de módulos, una clase base abstracta con cinco métodos y un mecanismo de configuración por evento.
Y dentro de todo eso, el trabajo real —el código que de verdad busca un asiento libre y lo marca como ocupado— son nueve líneas:
# Archivo: plugins/impls/default_seating.py (fragmento: el único trabajo real de la carpeta)
def assign_seat(self, order, ticket):
# Si el boleto ya trae asiento (por ejemplo, el cliente lo eligió en el mapa), respétalo.
if ticket.seat is not None:
return ticket.seat
row = query(
"SELECT label FROM seats WHERE event_id = ? AND taken = 0 ORDER BY label LIMIT 1",
ticket.event_id,
)
if row is None:
return None # el evento es de admisión general: no hay asientos
execute("UPDATE seats SET taken = 1 WHERE event_id = ? AND label = ?",
ticket.event_id, row.label)
return row.label
Nueve líneas de trabajo dentro de ciento ochenta y tres líneas de estructura. Veinte líneas de andamiaje por cada línea que hace algo.
Pero el número de líneas es lo menos grave. Lo grave es lo que cuesta responder una pregunta. Imagina que entras al equipo el lunes y alguien te dice: "un cliente reclama que le asignaron el asiento A-1 cuando pidió pasillo; averigua cómo se elige el asiento". Este es el camino que tienes que recorrer:
checkout/checkout.py
│ plugin = get_seating_plugin(event)
▼ ← salto 1: ¿qué es get_seating_plugin?
plugins/registry.py · get_for(event)
│ discover()
▼ ← salto 2: ¿qué descubre y cuándo?
plugins/registry.py · discover()
│ importlib.import_module(f"plugins.impls.{module_name}")
▼ ← salto 3: importa módulos POR NOMBRE.
│ Tu editor no puede seguir esto.
plugins/impls/default_seating.py
│ @register
▼ ← salto 4: el import ejecuta un decorador
plugins/registry.py · register()
│ _REGISTRY[instance.name()] = instance
▼ ← salto 5: y de vuelta a config.py
plugins/config.py · get_configured_name(event)
│ return "default"
▼ ← salto 6: por fin, el método
_REGISTRY["default"].assign_seat(order, ticket)
Seis saltos. Cuatro archivos abiertos. Y un detalle que solo se sufre con las manos en el teclado: en el salto 6, cuando le pides a tu editor "llévame a la definición de assign_seat", te lleva a plugins/base.py —al método abstracto, el que solo dice raise NotImplementedError—. Porque para el editor, el tipo de esa variable es la interfaz, no la implementación. Tienes que buscar a mano quién la implementa.
Qué esperar de este recorrido. Lo primero: comprobar que el costo no es teórico. Poner un número a "esto es difícil de leer" cambia la conversación. Cuatro archivos y seis saltos para responder "¿cómo se elige el asiento?", cuando la respuesta cabe en nueve líneas. Si el mismo código estuviera en un archivo llamado seating.py con una función llamada assign_seat, el recorrido sería: abrir el archivo, leer la función. Un salto.
Lo segundo, y esto es lo que suele sorprender: esta estructura no está mal escrita. Al contrario. Está bien escrita. Los nombres son buenos, las responsabilidades están separadas, cada archivo es corto y hace una cosa, hay documentación en los métodos abstractos. Quien la escribió sabía programar. El problema no es la calidad de la ejecución; es que se ejecutó algo que no hacía falta. Esa distinción es incómoda porque significa que no puedes detectar el problema mirando si el código "se ve bien". Se ve bien. Tienes que preguntar otra cosa.
Y lo tercero, que es el remate: en los dos años que lleva existiendo esa carpeta, se han registrado cero plugins nuevos. La columna seating_plugin de la tabla de eventos siempre ha valido "default", en los ciento ochenta mil eventos que Boletia ha publicado. El método supports(event) de la única implementación devuelve True para todo. El mecanismo de extensión se construyó, se probó, se documentó, y nunca se extendió nada.
Hay tres commits sobre esa carpeta en dos años, y ninguno agrega funcionalidad:
- Renombrar
plugins/impl/aplugins/impls/, porque el nombre estaba en singular y el descubrimiento lo buscaba en plural. - Arreglar
pkgutil.iter_modulesdespués de subir de Python 3.9 a 3.11, porque el__path__del paquete dejó de comportarse igual. - Agregar un
log.debugendiscover()para investigar por qué la primera compra después de cada despliegue tardaba casi medio segundo más que las demás.
Detente en el tercero un segundo, porque es delicioso. El descubrimiento es perezoso: ocurre en la primera petición que necesita un asiento, no al arrancar la aplicación. Así que después de cada despliegue, el primer cliente que compra paga el costo de importar los módulos. Ese es un bug de rendimiento que solo puede existir porque existe la abstracción. Sin el registro dinámico, no hay descubrimiento; sin descubrimiento, no hay primera petición lenta.
Ese es el cuarto costo de la abstracción que vamos a nombrar formalmente en la lección 2: una capa nueva es también una superficie nueva donde pueden vivir bugs que no existirían sin ella.
La idea que ordena el módulo
Aquí está la frase que quiero que te lleves de la lección, y que es el título mismo: el buen diseño se mide tanto por lo que pusiste como por lo que decidiste no poner.
Esa idea es difícil de aceptar por una razón muy humana: lo que no pusiste no se ve. Nadie en una revisión de código te felicita por la interfaz que no creaste. No hay un diagrama de la fábrica que decidiste que sobraba. Si haces bien tu trabajo, el resultado se parece sospechosamente a no haber hecho nada: una función simple, en un archivo, con un nombre claro.
Compara eso con el incentivo del otro lado. Meter una arquitectura de plugins es visible, se puede presentar en una reunión, se ve sofisticado, se puede poner en el currículum. Y —esto es lo peor— no tiene consecuencias inmediatas. El costo llega meses después, repartido en pedacitos de tres minutos cada vez que alguien intenta leer el código. Nadie conecta esos tres minutos con la decisión de hace dos años.
En economía hay un nombre para esto: los beneficios están concentrados y los costos están difusos. Quien mete la abstracción cosecha el reconocimiento hoy; el equipo paga la factura en cuotas invisibles durante años. Y quien paga casi nunca es el mismo que decidió.
Por eso este módulo empieza por aquí, y por eso la capacidad que quiero dejarte es una capacidad de decir que no con argumentos. No de decir que no por reflejo —eso es otra forma de no pensar—. De decir: "aquí no, y estas son las tres razones, y este es el escenario concreto en el que cambiaría de opinión". Esa última parte, el escenario en el que cambiarías de opinión, es lo que convierte una opinión en criterio.
La pregunta de bolsillo
Si de todo el módulo te llevas una sola herramienta, que sea esta pregunta:
¿Cuántas implementaciones distintas existen hoy, de verdad?
No "cuántas podría haber". No "cuántas imaginamos que va a haber". Cuántas hay hoy, contadas con el dedo, en el código que está corriendo en producción.
- Si la respuesta es una, casi nunca hace falta una abstracción. Escribe la cosa directamente.
- Si la respuesta es dos, todavía no sabes cuál es el eje de variación real. La lección 3 explica por qué, y por qué abstraer aquí es el momento de máximo riesgo.
- Si la respuesta es tres o más, ahora sí tienes información suficiente para ver qué comparten de verdad. Aquí es donde un patrón empieza a ganarse su lugar.
La pregunta es tan simple que parece tonta, y funciona precisamente por eso: es difícil de contestar mal. Cuando alguien propone una interfaz y le preguntas cuántas implementaciones hay hoy, la conversación cambia de registro. Ya no se discute sobre estética ni sobre "buenas prácticas": se discute sobre un número que los dos pueden contar.
En el rincón de plugins de Boletia, la respuesta es una. Lo fue el día que se escribió y lo sigue siendo dos años después.
Por qué el freno va antes que el acelerador
Vale la pena hacer explícito el argumento pedagógico, porque probablemente sea el orden inverso al de cualquier otro material que hayas visto.
Imagina que la guía empezara por el módulo 3 y te enseñara Strategy en la segunda semana. Strategy es un patrón excelente: separa el "qué se hace" del "cómo se hace" y convierte un condicional que crece en un conjunto de clases pequeñas. Lo entenderías bien, verías el ejemplo de las reglas de precio de Boletia, y quedarías convencido —con razón— de que ahí se gana su lugar.
Y después abrirías tu propio proyecto en el trabajo.
Lo que pasa entonces está documentado en la industria hasta el cansancio, y tiene nombre: el síndrome del martillo nuevo. Herramienta recién aprendida, ganas de usarla, y una habilidad todavía tierna para distinguir dónde aplica. El if/else de dos ramas que funcionaba perfectamente se convierte en una interfaz, dos clases y una fábrica. No porque seas descuidado: precisamente porque estás poniendo atención y quieres hacerlo bien.
El antídoto no es enseñar el patrón con más cuidado. El antídoto es instalar el criterio antes, para que cuando el patrón llegue, llegue a una cabeza que ya tiene una pregunta puesta. Que la primera reacción frente a un if no sea "aquí cabe una Strategy" sino "¿cuántos casos hay hoy?".
Hay una segunda razón, más práctica. En un equipo real, la mayoría de las decisiones sobre patrones no son "¿cuál pongo?" sino "¿pongo alguno?". Y una porción grande del trabajo de un desarrollador con experiencia no es introducir estructura: es quitar la que sobra, o defender que no se agregue más. Si toda tu formación fue en la dirección de agregar, no tienes vocabulario para la otra mitad del trabajo.
El mapa de las siete lecciones que siguen
| # | Lección | Qué instala | Qué te llevas |
|---|---|---|---|
| 2 | Cada abstracción se paga | Los cuatro costos concretos de una capa: un salto, un archivo, un concepto, una superficie de bug | Poder decir cuánto cuesta, no solo que cuesta |
| 3 | La regla de tres | La heurística que evita la mayor parte de la sobre-ingeniería, y el concepto de eje de variación | Saber cuándo tienes información suficiente para abstraer |
| 4 | Abstracción prematura | La comparación honesta entre duplicar y abstraer mal, y el ciclo de degradación | Preferir la duplicación visible al acoplamiento invisible |
| 5 | YAGNI | El principio con su matiz, y la línea entre preparación reversible e infraestructura especulativa | Distinguir lo barato de deshacer de lo que se queda para siempre |
| 6 | El plugin para una sola implementación | El anti-patrón completo: cómo se llega, qué cuesta, cómo se desmonta | Reconocer el olor y tener el procedimiento de desmontaje |
| 7 | Acoplamiento contra indirección | El tradeoff de fondo y los tres ejes de la decisión | Elegir con argumentos, sabiendo que ninguna opción es gratis |
| 8 | Proyecto | La práctica sobre el rincón real de Boletia | Un antes/después y una justificación defendible |
Fíjate en la forma del arco. Las lecciones 2 a 5 construyen el criterio en abstracto, cada una con su pieza. La 6 lo aplica a un caso vivo y completo. La 7 sube un nivel y muestra que todo el módulo era, en el fondo, un solo tradeoff visto desde cuatro ángulos. Y la 8 te pone a hacerlo.
Lo que este módulo NO dice
Conviene ser explícito, porque un módulo titulado "cuándo no abstraer" se malinterpreta con facilidad, y la malinterpretación es tan cara como el problema original.
Este módulo no dice que las abstracciones sean malas. Boletia tiene cuatro lugares donde una abstracción se gana limpiamente su lugar: las reglas de precio, los proveedores de pago, los canales de notificación y los exportadores de reportes. En los cuatro hay tres o más implementaciones reales, con lógicas de verdad distintas, y en los cuatro la abstracción paga con creces lo que cuesta. Los módulos 3 a 6 se dedican precisamente a eso.
Este módulo no dice que "simple" signifique "todo en un archivo". Un checkout de trescientas líneas con un if por cada proveedor de pago tampoco está bien, y en el proyecto final de la guía vas a arreglarlo introduciendo estructura. La sub-estructura y la sobre-estructura son dos formas de errar; este módulo se ocupa de la segunda porque es la que casi nadie enseña.
Este módulo no dice que no pienses en el futuro. Dice que no construyas para el futuro que no está pedido. Hay una diferencia enorme entre poner una llamada a la librería de correo detrás de una función —diez minutos, reversible en diez minutos más— y montar un sistema de plugins con descubrimiento dinámico. La lección 5 dedica su espacio entero a esa línea.
Y este módulo no dice que quites toda abstracción que encuentres. Hay código feo que conviene dejar en paz, y hay abstracciones que hoy sobran pero cuyo desmontaje cuesta más de lo que ahorra. El módulo 8 tiene una lección completa sobre cuándo no tocar nada. Criterio, otra vez, no reflejo.
Errores comunes
Leer "no abstraigas" como una regla en vez de como una pregunta (conceptual). Qué pasa: alguien sale del módulo con la consigna "las abstracciones son malas" y empieza a rechazar toda propuesta de estructura en las revisiones, con la misma falta de criterio con la que antes las aceptaba todas. Cambió una postura automática por otra. Por qué pasa: las reglas son más fáciles de recordar que los criterios, y el módulo tiene un título que suena a regla. Cómo detectarlo: si tu argumento en una discusión es "eso es sobre-ingeniería" y no puedes decir cuántas implementaciones hay hoy ni qué costaría el cambio si llega, no tienes un argumento: tienes un eslogan. Cómo corregirlo: obligarte a acompañar cada "no" con dos cosas —el número de implementaciones reales y el escenario concreto que te haría cambiar de opinión—. Si no puedes producir esas dos cosas, todavía no tienes una posición.
Confundir "se ve bien escrito" con "hace falta" (de diagnóstico). Qué pasa: alguien revisa el rincón de plugins de Boletia, ve archivos cortos, nombres claros, responsabilidades separadas y docstrings en cada método, y concluye que está bien. Y en cierto sentido lo está: la ejecución es buena. Lo que no se preguntó es si había que ejecutarlo. Por qué pasa: casi toda la formación en calidad de código enseña a evaluar cómo está escrito algo —nombres, longitud, cohesión, tests— y muy poca enseña a evaluar si debería existir. Son dos preguntas distintas y la segunda es la que más ahorra. Cómo detectarlo: cuando estés a punto de aprobar un cambio porque "está limpio", detente y pregunta cuántos casos reales atiende. Cómo corregirlo: separar explícitamente las dos preguntas en tu revisión. Primero ¿debería existir esto?, y solo si la respuesta es sí, ¿está bien hecho?. En ese orden, porque el segundo trabajo se desperdicia si el primero falla.
Creer que el costo de una abstracción se paga una vez (de modelo mental). Qué pasa: alguien evalúa la propuesta de meter el sistema de plugins pensando "son tres días de trabajo, y la flexibilidad vale tres días". El cálculo parece razonable y está mal en su forma, no en sus números. Por qué pasa: estamos entrenados para pensar en el costo de construir, que es visible, cotizable y ocurre una vez. El costo de la indirección no funciona así: se paga en cada lectura, por cada persona, durante toda la vida del código. Tres minutos por lectura, seis personas en el equipo, unas cuantas lecturas al mes, durante dos años, es un número mucho más grande que tres días —y no aparece en ninguna estimación—. Cómo detectarlo: si tu justificación empieza con "solo son X días", estás midiendo la variable equivocada. Cómo corregirlo: pregunta siempre por el costo recurrente, no por el inicial. La lección 2 le pone nombre y forma a esa cuenta, y la lección 5 la extiende con el costo de mantener algo que nadie usa.
Ejercicios
Ejercicio 1 — Cuenta los saltos de tu propio código. Abre un proyecto en el que trabajes hoy (o el último que hayas tocado) y elige una funcionalidad pequeña que un usuario pueda describir en una frase: "cuando aprieto guardar, ¿qué pasa?", "cuando llega un correo, ¿quién lo procesa?". Sigue el flujo con el dedo desde el punto de entrada hasta la línea que hace el trabajo de verdad, y anota dos números: cuántos archivos tuviste que abrir y cuántos saltos de un lugar a otro diste. Después, para cada salto, escribe en tres palabras qué te dio ese salto a cambio.
Ver solución
No hay una respuesta correcta, pero hay una forma reconocible en el resultado.
Sobre los números: en código sano, una funcionalidad pequeña suele resolverse en uno a tres archivos y dos a cuatro saltos. Si llegaste a seis o más archivos para algo que un usuario describe en una frase, tienes un candidato a revisar. Ojo con no confundirte: los saltos que atraviesan fronteras reales —de la capa HTTP a la lógica, de la lógica a la base de datos— están comprados y pagados; son los que separan cosas que de verdad son distintas. Los que hay que mirar con lupa son los saltos que te dejan en el mismo nivel conceptual, solo que en otro archivo.
Sobre la tercera parte, que es la que enseña: para cada salto tenías que escribir qué te dio a cambio. Vas a descubrir que en la mayoría puedes contestar sin esfuerzo —"aísla la base de datos", "permite cambiar el proveedor de correo", "separa la validación"— y que en uno o dos te vas a quedar en blanco, o vas a escribir algo como "no sé, siempre estuvo ahí". Ese salto sin respuesta es el interesante. No significa automáticamente que sobre; significa que nadie en el equipo puede articular qué compra, y una abstracción que nadie puede justificar es una abstracción que nadie va a poder defender ni quitar.
Por qué funciona: acabas de convertir una sensación —"este código es enredado"— en dos números y una lista. Esa traducción es la mitad del trabajo de este módulo, porque los números se pueden discutir en equipo y las sensaciones no.
Ejercicio 2 — Aplica la pregunta de bolsillo a Boletia. Para cada uno de estos cinco rincones de Boletia, contesta "¿cuántas implementaciones distintas existen hoy?" y di si esperarías encontrar una abstracción justificada, dudosa o claramente de más: (a) pricing/ — las reglas de precio; (b) payments/ — los proveedores de pago; (c) notifications/ — los canales de aviso; (d) reports/ — los exportadores; (e) plugins/ — el mapa de asientos.
Ver solución
(a) pricing/: cuatro. General, VIP, early-bird y cortesía. Cuatro comportamientos de verdad distintos para la misma pregunta —"¿cuánto cuesta este boleto?"—, y no son variaciones cosméticas: uno multiplica, otro depende de una fecha, otro tiene un tope de emisiones. Abstracción justificada. Es el terreno del módulo 3.
(b) payments/: tres. Y la tercera es especialmente reveladora, porque el pago en efectivo ni siquiera cobra: genera una referencia y devuelve un estado pendiente. Abstracción justificada, con una advertencia que la lección 3 va a desarrollar: si esta abstracción se hubiera diseñado cuando solo existían dos proveedores, probablemente estaría en el eje equivocado.
(c) notifications/: tres. Correo, SMS y push. Tres canales reales, con la particularidad de que no todos están disponibles para todos los clientes —hay quien no dio teléfono y quien no tiene la app—. Abstracción justificada; es el terreno del módulo 6.
(d) reports/: tres. CSV, PDF y hoja de cálculo. Los tres hacen exactamente los mismos pasos en el mismo orden y solo cambian en el paso del formato. Abstracción justificada, y de un tipo específico —mismo esqueleto, un paso distinto— que tiene su propio nombre en el módulo 3.
(e) plugins/: una. DefaultSeatingPlugin, y su método supports(event) devuelve True para cualquier evento, lo cual es la confesión escrita de que nunca hubo intención de que hubiera un segundo. Abstracción claramente de más. Es el caso de la lección 6 y del proyecto.
Por qué funciona: cuatro de cinco rincones de Boletia tienen abstracción y la merecen. Eso importa tanto como el quinto. Si sales de este módulo creyendo que abstraer suele ser un error, no aprendiste el criterio: cambiaste un prejuicio por otro. Lo que distingue al quinto no es que tenga una abstracción, es que tiene una implementación.
Ejercicio 3 — Escribe el escenario que te haría cambiar de opinión. Toma el rincón plugins/ de Boletia y escribe, en tres o cuatro frases, la condición concreta bajo la cual sí valdría la pena tener ahí un mecanismo de extensión. Sé específico: no vale "si en el futuro hay más plugins". Tiene que ser algo que alguien pueda verificar el lunes siguiente mirando el sistema o el roadmap.
Ver solución
Una respuesta bien formada se parece a esto:
Valdría la pena si existieran al menos tres algoritmos de asignación de asiento realmente distintos y en uso: por ejemplo, "primer libre" para eventos generales, "mejor asiento disponible" para teatros con numeración por filas y calidad de visión, y "asientos contiguos para el grupo completo" para compras múltiples. Además, tendría que haber un requisito verificable de que un tercero fuera del equipo —un organizador grande con su propio sistema de butacas— necesita conectar el suyo sin que nosotros despleguemos código. Si esas dos condiciones se cumplen, el mecanismo de extensión se gana su lugar. Mientras solo se cumpla la primera, alcanza con un diccionario literal que mapee nombre a función, sin descubrimiento dinámico.
Fíjate en tres propiedades de esa respuesta. Primero, es verificable: alguien puede abrir el roadmap o preguntarle a producto y decir sí o no. "Si en el futuro hay más plugins" no es verificable; es un deseo. Segundo, distingue dos niveles de necesidad —varios algoritmos internos, y terceros que cargan código sin desplegar—, y solo el segundo justifica el descubrimiento dinámico. El primero se resuelve con un diccionario de tres entradas. Esa distinción es exactamente el espectro que vamos a desplegar en la lección 7. Y tercero, deja dicho qué se haría en el caso intermedio, que es el caso más probable.
Por qué funciona: este ejercicio es el que convierte una opinión en criterio. Cualquiera puede decir "esto sobra". Quien puede decir "esto sobra, y volvería a ponerlo si pasara exactamente esto" está haciendo ingeniería. Además te prepara para el entregable del proyecto de la lección 8, que pide precisamente este párrafo.
Resumen y siguiente paso
En esta lección viste por qué el módulo del freno va antes que el módulo del acelerador: enseñar Strategy antes de enseñar cuándo no usar Strategy es la receta del martillo nuevo, y produce sobre-ingeniería con nombres elegantes.
Viste la analogía que va a acompañarnos: cada capa de indirección es una escala en un vuelo. Te da la opción de bajarte en el camino, y te cobra un despegue y un aterrizaje en cada viaje, uses la opción o no.
Conociste con detalle el rincón plugins/ de Boletia: cinco archivos, ciento ochenta y tres líneas, seis saltos y cuatro archivos abiertos para responder cómo se elige un asiento, todo alrededor de nueve líneas de trabajo real y de una sola implementación cuyo supports() devuelve True para todo. Y viste el detalle más incómodo del caso: ese código está bien escrito. La calidad de la ejecución no te dice nada sobre si debía ejecutarse.
Te llevas la pregunta de bolsillo —¿cuántas implementaciones distintas existen hoy?— y las cuatro cosas que este módulo no dice, para que no lo conviertas en un eslogan.
Antes de avanzar deberías poder: explicar en una frase por qué el módulo 2 va antes que el 3; describir qué hay en plugins/ y cuánto cuesta leerlo; y aplicar la pregunta de bolsillo a un rincón de código cualquiera y sacar una conclusión provisional.
Lo que todavía no tenemos es el desglose fino del costo. Hoy dijimos "seis saltos" y "tres minutos por lectura", pero eso es una anécdota, no un modelo. La lección 2 desarma el costo de una abstracción en sus cuatro componentes concretos —el salto, el archivo, el concepto y la superficie de bug— y te deja una forma de estimarlo que puedes usar en una discusión de equipo sin depender de que el otro comparta tu intuición.
Recursos
- The Grug Brained Developer — un ensayo humorístico y sorprendentemente serio sobre la complejidad como el enemigo real del desarrollador. Es, en el fondo, este módulo entero contado como comedia. Empieza por la sección sobre el "factoring".
- Write code that is easy to delete, not easy to extend (tef) — el ensayo que invirtió el marco para mucha gente: optimizar por facilidad de borrado en vez de por facilidad de extensión. Corto y denso.
- YAGNI (Martin Fowler) — la formulación de referencia del principio que veremos en la lección 5, con su desglose de costos. Dos páginas.
- Refactoring Guru — Design Patterns — el diccionario visual de consulta para el resto de la guía. Útil como referencia, no como plan de estudio; este módulo existe precisamente porque leerlo de corrido produce el efecto contrario al deseado.