Módulo 1: Qué son de verdad los patrones
7. Cómo NO estudiar patrones
Descripción
Al terminar esta lección vas a conocer, dichos de frente, los cuatro anti-métodos que arruinan el estudio de los patrones: memorizar diagramas, salir a buscar dónde aplicar lo que acabas de aprender, contar patrones como si fueran puntos, y estudiar la forma sin el costo. Y vas a tener los cuatro hábitos que los reemplazan, más una prueba de tres preguntas que puedes aplicar antes de introducir cualquier estructura en cualquier código.
Esto importa porque a estas alturas del módulo hay un riesgo real y muy documentado. Acabas de recibir un mapa con ocho nombres frescos. La lección anterior te enseñó a reconocerlos y te mostró dónde vive cada uno. El efecto normal de eso —normal, no vergonzoso— es que durante las próximas semanas todo el código que leas va a parecer pedir alguno. Vas a ver un if de dos ramas y pensar "Strategy". Vas a ver una función que llama a otras dos y pensar "Facade". Ese efecto tiene nombre, tiene historia y le ha costado a la industria una cantidad enorme de código innecesariamente complicado.
Esta es, además, la lección bisagra del módulo. Todo lo anterior construyó una capacidad: reconocer y nombrar estructura. Esta lección instala el freno que esa capacidad necesita, y se lo entrega al módulo 2, que trata completo sobre cuándo no abstraer. Sin este freno, lo que aprendiste hasta aquí es un acelerador sin volante.
Quiero decir algo antes de empezar, porque el tono de esta lección puede leerse como advertencia moralista y no lo es. Los cuatro anti-métodos no son errores de gente descuidada. Son errores de gente entusiasta, que estudió y quiere aplicar lo que estudió. El código sobre-diseñado del mundo no lo escribieron programadores flojos: lo escribieron programadores aplicados en el momento equivocado de su aprendizaje. Reconocer eso es lo que permite corregirlo sin ponerse a la defensiva.
Conexión con el módulo: la lección 6 te dio el mapa; esta te dice cómo no usarlo. La lección 5 te dio la técnica de reconocimiento; esta advierte contra su exceso —ver patrones donde no los hay—. Y el proyecto de la lección 8 está diseñado con esta lección adentro: te pide nombrar las estructuras de Boletia sin cambiar una sola línea, precisamente para que practiques la habilidad sin caer en la tentación de aplicarla. Hacia adelante, el módulo 2 desarrolla en ocho lecciones lo que aquí se enuncia en una.
El martillo nuevo
Hay una frase que se le atribuye al psicólogo Abraham Maslow y que dice, más o menos: si la única herramienta que tienes es un martillo, todo empieza a parecerse a un clavo. Se usa tanto que casi perdió el filo, pero describe con precisión lo que le pasa a alguien que acaba de aprender algo.
Piensa en quien compra su primera sierra caladora —esa que corta curvas—. Durante el primer mes, cada proyecto de la casa aparece con una curva. El librero necesita bordes redondeados. El escritorio necesita una hendidura ergonómica. El estante de la cocina, que iba a ser un rectángulo, ahora tiene una forma orgánica. No es que esas curvas mejoren nada: es que la herramienta nueva reorganizó su forma de ver los problemas.
Y aquí está lo que hace interesante el fenómeno: la persona no está siendo tonta. Está haciendo justo lo que la gente hace cuando aprende: buscar oportunidades de practicar. Es un impulso bueno en casi todos los contextos —así se consolida cualquier habilidad—. El problema es que en el software, "practicar" significa dejar el resultado en un repositorio que otras personas van a mantener durante años. La curva innecesaria del librero se queda en tu casa. La abstracción innecesaria en el código se queda en el trabajo de todo tu equipo.
Con los patrones el efecto es especialmente fuerte por una razón adicional: el patrón viene con la solución antes que con el problema. Cuando aprendes a leer código, aprendes una habilidad de observación. Cuando aprendes un patrón, recibes una solución completa, con su forma y su nombre, sin haber sentido nunca el problema que la motivó. Tener una solución en la cabeza y no tener su problema es, literalmente, ir por el mundo buscando dónde encaja.
Vamos a ver cómo se ve eso cuando ocurre de verdad.
Ejemplo trabajado: cómo nació el rincón plugins/ de Boletia
Llevamos siete lecciones señalando plugins/ como el rincón sobre-diseñado. Ahora vamos a reconstruir cómo llegó ahí, porque la historia es más instructiva que el veredicto.
Hace dos años, Boletia necesitaba asignar asientos numerados. Hasta entonces todos los eventos eran de admisión general —entras y te sientas donde puedas—, pero un teatro pidió venta con mapa de asientos. Lo que se necesitaba era esto:
# Lo que el problema pedía, en su forma más honesta.
# Archivo: seating.py
def assign_seat(event, order):
"""Reserva el mejor asiento disponible para esta orden."""
available = repository.available_seats(event.id)
if not available:
raise NoSeatsAvailable(event.id)
# "Mejor" hoy significa: el más cercano al escenario entre los disponibles.
best = min(available, key=lambda s: s.distance_to_stage)
repository.reserve_seat(best.id, order.id)
return best
Doce líneas. Una función. Resolvía el problema del teatro completo.
Lo que se escribió fue otra cosa. La persona a cargo —con buenas intenciones, buen nivel técnico y un curso de patrones recién terminado— razonó así, y quiero reproducir el razonamiento porque es impecable en cada paso y equivocado en conjunto:
"El criterio de 'mejor asiento' va a cambiar según el tipo de evento. Un teatro quiere cerca del escenario. Un estadio quiere agrupar a los que compran juntos. Un cine quiere el centro. Además, cada recinto puede tener su propia forma de numerar. Esto claramente va a tener varias implementaciones. Y como los recintos son de terceros, quizás algún día ellos quieran aportar la suya. Voy a hacerlo extensible desde el principio: interfaz abstracta, registro de implementaciones y carga dinámica, para que agregar una nueva no requiera tocar el núcleo."
Y escribió esto:
# ============ plugins/base.py ============
class SeatingPlugin:
"""Contrato que toda estrategia de asignación de asientos debe cumplir."""
def assign(self, event, order): raise NotImplementedError
def release(self, seat_id): raise NotImplementedError
def layout_for(self, event): raise NotImplementedError
def validate_layout(self, layout): raise NotImplementedError
def supports_venue(self, venue_type): raise NotImplementedError
# ============ plugins/registry.py ============
class SeatingPluginRegistry:
_plugins = {}
@classmethod
def register(cls, name, plugin_cls):
cls._plugins[name] = plugin_cls
@classmethod
def get(cls, name):
# Carga el módulo por nombre y confía en que se auto-registre al importarse.
if name not in cls._plugins:
importlib.import_module(f"plugins.impls.{name}")
return cls._plugins[name]()
# ============ plugins/impls/default_seating.py ============
class DefaultSeating(SeatingPlugin):
def assign(self, event, order):
available = repository.available_seats(event.id)
if not available:
raise NoSeatsAvailable(event.id)
best = min(available, key=lambda s: s.distance_to_stage)
repository.reserve_seat(best.id, order.id)
return best
def release(self, seat_id):
repository.free_seat(seat_id)
def layout_for(self, event):
return repository.layout(event.id)
def validate_layout(self, layout):
return True # nunca se implementó de verdad
def supports_venue(self, venue_type):
return True # siempre dice que sí
SeatingPluginRegistry.register("default_seating", DefaultSeating)
Y en el checkout:
# checkout/checkout.py — cómo se usa
plugin = SeatingPluginRegistry.get(settings.SEATING_PLUGIN) # siempre "default_seating"
seat = plugin.assign(event, order)
Dos años después: una implementación. El estadio nunca llegó. El cine nunca llegó. Ningún recinto de terceros pidió aportar nada. validate_layout devuelve True sin mirar nada y supports_venue también. Y SEATING_PLUGIN vale "default_seating" en los tres ambientes desde el primer día.
Qué esperar de esta reconstrucción. Cinco cosas, y ninguna es "esa persona era mala".
La primera: cada paso del razonamiento era plausible. "El criterio va a cambiar según el evento" es una predicción razonable. "Quizás terceros quieran aportar" también. Lo que falló no fue la lógica: fue tratar predicciones como si fueran requisitos. Ninguna de las dos se cumplió, y no había forma de saberlo entonces —ese es exactamente el punto—.
La segunda: el costo no fue solo escribir de más. Doce líneas se convirtieron en unas sesenta repartidas en tres archivos. Pero el costo real es continuo: durante dos años, cada persona que necesitó entender cómo se asigna un asiento tuvo que leer tres archivos, entender un mecanismo de registro, y descubrir por su cuenta que solo hay una implementación. Multiplica eso por cada persona nueva del equipo. Y hay un costo peor: la carga dinámica por nombre de módulo rompe la navegación del editor. No puedes hacer "ir a la implementación" porque el vínculo se arma en tiempo de ejecución. La herramienta que usarías para orientarte quedó ciega justo en el rincón que más lo necesita.
La tercera: el diseño quedó peor de lo que habría quedado esperando. Fíjate en SeatingPlugin: cinco métodos, dos de los cuales nunca hicieron nada. Se inventaron para un futuro imaginado, y el futuro imaginado tenía una forma distinta a la del futuro real. Si el estadio hubiera llegado de verdad, es muy probable que la interfaz no le hubiera servido —porque agrupar compras juntas no es "asignar un asiento", es asignar varios con restricciones entre sí, y ningún método del contrato contempla eso—. Este punto es sutil y muy importante: abstraer temprano no solo cuesta, también suele producir la abstracción equivocada, porque se diseña con una sola muestra.
La cuarta: nadie lo quitó. Es el efecto más pernicioso. Una vez que existe, quitarlo requiere que alguien lo entienda, se convenza de que es seguro y se atreva. Es más fácil rodearlo. Así se acumulan los rincones que nadie toca.
Y la quinta, la que da sentido a toda esta lección: el problema no fue el patrón. La extensibilidad por plugins es una idea legítima que resuelve problemas reales en sistemas que de verdad reciben implementaciones de terceros. El problema fue el orden: la solución llegó antes que el problema. Es el mismo payments/ que sí funciona, con la misma forma, en el momento equivocado.
Los cuatro anti-métodos
Anti-método 1: memorizar diagramas.
Qué es: estudiar cada patrón como una figura —cajas, flechas, nombres de clase genéricos como ConcreteStrategyA— hasta poder reproducirla.
Por qué falla: el diagrama es la casilla 2 de la lección 2, y solo esa. No contiene el problema —lo que aparece en el dibujo es la solución ya aplicada— y no contiene las consecuencias, que ni siquiera son dibujables. Estudiar el diagrama es estudiar la parte del patrón que menos decide.
Hay un fallo adicional, más práctico: el diagrama usa nombres genéricos, y el código real usa nombres del dominio. Quien memorizó ConcreteStrategyA no reconoce VipPricing como lo mismo, porque lo que aprendió fue la figura y no la idea. Es exactamente el rastreador novato de la lección 5, comparando fotos de huellas.
Y un tercero, que se nota al implementar: el diagrama sugiere que hay una forma canónica y única. No la hay. Recuerda la definición de Alexander —usar la solución un millón de veces sin hacerla nunca dos veces igual—. Quien memorizó la figura tiende a reproducirla completa, incluidos los pedazos que su caso no necesita.
Anti-método 2: salir a buscar dónde aplicar lo que acabas de aprender.
Qué es: terminar de estudiar un patrón y abrir el código propio con la pregunta "¿dónde puedo usar esto?".
Por qué falla: invierte el orden que la lección 4 estableció. Los patrones se descubren porque el problema empuja; buscarles lugar es tener una solución y salir a conseguirle un problema. Y como todo código tiene alguna variación, algún condicional y alguna clase que llama a otra, siempre vas a encontrar dónde encaja. Esa es la trampa: el anti-método siempre tiene éxito aparente.
La versión más dañina de esto es la que le pasó a Boletia, porque ocurre en el momento de mayor libertad: cuando estás escribiendo algo nuevo y nadie te va a detener.
Anti-método 3: contar patrones como si fueran puntos.
Qué es: la creencia —casi nunca explícita, casi siempre presente— de que un código con más patrones está mejor diseñado que uno con menos, y de que un programador que usa más patrones es mejor.
Por qué falla: es medir la variable equivocada. Un buen diseño se mide por cuánto cuesta cambiarlo, no por cuántas estructuras nombradas contiene. Y la relación entre las dos cosas no es lineal: sube un rato y después baja, porque cada patrón agrega indirección y la indirección tiene un techo antes de volverse un obstáculo.
Este anti-método tiene una raíz social que conviene nombrar. En entrevistas de trabajo se pregunta "¿qué patrones conoces?" y no "¿qué abstracción quitaste el año pasado?". En revisiones de código, proponer una estructura parece iniciativa y decir "está bien así" parece conformismo. Esa asimetría empuja a todo el mundo en la misma dirección, y por eso hace falta decirlo explícito: quitar una abstracción innecesaria es una contribución técnica de la misma categoría que agregar una necesaria, y en equipos maduros se valora igual.
Anti-método 4: estudiar la forma sin el costo.
Qué es: aprender qué hace un patrón y cómo se arma, sin aprender qué se paga por él.
Por qué falla: ya lo desarrollamos en la lección 2, pero aquí importa como anti-método porque es el que habilita a los otros tres. Quien conoce el costo del Observer no lo mete para dos destinatarios: la propia casilla de consecuencias lo frena. El martillo nuevo pega tan fuerte precisamente porque el martillo se estudió sin su peso.
Hay una variante de este anti-método que merece mención aparte: aprender del ejemplo de juguete. Casi todo el material sobre patrones usa ejemplos artificiales —figuras geométricas que calculan su área, animales que hacen su sonido, patos que vuelan o no vuelan—. Esos ejemplos son útiles para explicar la mecánica en tres minutos, y son pésimos para desarrollar criterio, por una razón: en el ejemplo de juguete el patrón siempre vale la pena. Están construidos para eso. Nunca vas a ver un tutorial que termine con "…y por eso aquí conviene dejar el if". Quien aprendió solo con ejemplos de juguete tiene una muestra sesgada al cien por ciento a favor.
Los cuatro hábitos que sí funcionan
Hábito 1: parte del problema, siempre.
En la práctica: cuando quieras estudiar patrones, no abras un catálogo. Abre código. Puede ser el de tu trabajo, el de un proyecto de código abierto que uses, o el de Boletia en el proyecto de la próxima lección. Recorre, encuentra estructuras, formula el problema de cada una en una frase de dolor concreto, y solo entonces busca si tiene nombre.
Por qué funciona: es el orden en que los patrones fueron descubiertos, y es el orden en que se usan en el trabajo. Además, cuando el nombre llega después de la frase del problema, se queda pegado a un caso real. Lo que se memoriza en abstracto se evapora; lo que se ancla en un caso concreto se recuerda años.
Hábito 2: aprende el costo junto con la forma, siempre en la misma sesión.
En la práctica: cada vez que estudies un patrón, no cierres el tema hasta poder responder dos preguntas —"¿qué tipo de cambio se vuelve caro con esto puesto?" y "¿cuántas variantes reales harían falta para que valga la pena?"—. Si no puedes, no lo sabes todavía.
Por qué funciona: mantiene las dos mitades juntas. La forma sin el costo es un acelerador; el costo sin la forma es parálisis. Juntas son criterio.
Hábito 3: practica el "no" tanto como el "sí".
En la práctica: cuando revises código —tuyo o ajeno—, busca deliberadamente lugares donde una estructura sobra, no solo donde falta. Es un músculo distinto y casi nadie lo entrena. Empieza por lo más fácil de detectar: interfaces con una sola implementación, fábricas que construyen un solo tipo, capas que solo delegan sin agregar nada.
Por qué funciona: corrige la asimetría social del anti-método 3. Y porque, honestamente, en la mayoría de las bases de código maduras hay más abstracción de sobra que de falta. Aprender a ver eso te vuelve útil rápido.
Hábito 4: deja que el patrón emerja del refactor, no del diseño inicial.
En la práctica: escribe primero la versión simple y directa. Cuando aparezca la segunda variante, un if está bien. Cuando aparezca la tercera —y sobre todo cuando la misma decisión empiece a repetirse en varios lugares—, ahí introduce la estructura. Para entonces tendrás tres ejemplos reales de cómo varía, que es la única forma de diseñar una abstracción que sirva.
Por qué funciona: es la historia del payments/ de Boletia, que salió bien, contra la del plugins/, que salió mal. Y resuelve el problema que vimos en el ejemplo trabajado: con una sola muestra se diseña la abstracción equivocada. Con tres, se ve qué es común de verdad.
Esta es la regla de tres, y el módulo 2 le dedica una lección entera con sus matices —porque no es un dogma y tiene excepciones legítimas, como el Adapter para una dependencia externa que ya se filtró por catorce archivos—.
La prueba de las tres preguntas
Antes de introducir cualquier estructura en cualquier código, hazte estas tres preguntas. Toman un minuto y evitan la mayoría de los plugins/ del mundo.
1. ¿Cuántas implementaciones reales existen hoy, en el repositorio, ahora mismo?
No cuántas podrían existir. Cuántas hay. Si la respuesta es una, la respuesta a la pregunta general casi siempre es "todavía no". La única excepción común es el Adapter cuando una dependencia externa ya contaminó muchos archivos, porque ahí el problema no es la variedad sino el contagio.
2. ¿Qué cambio concreto y solicitado se vuelve barato con esto puesto?
Concreto y solicitado. "Marketing pidió dos tipos de boleto para el trimestre" es concreto y solicitado. "Podríamos necesitar más proveedores" es un pronóstico. Si tu respuesta está en futuro condicional, no tienes un problema: tienes una hipótesis.
3. ¿Qué se vuelve más difícil, y quién lo va a pagar?
Cuenta archivos y saltos, como hicimos en la lección 2. Y responde la segunda mitad honestamente: quien lo va a pagar es la persona que entre al equipo el año que viene y tenga que entender ese rincón sin ti al lado.
Si las tres respuestas te dejan cómodo, adelante. Si la primera es "una" y la segunda está en futuro, guarda la idea y sigue con el if. No lo estás postergando por pereza: lo estás postergando porque todavía no tienes la información para diseñarlo bien. Esa es la diferencia entre esperar y no hacer nada.
El puente al módulo 2
Todo lo de esta lección se enuncia en una frase que va a ser el eje del módulo siguiente: cada abstracción se paga, y hay que comprarla con un problema real.
El módulo 2 desarrolla eso en ocho lecciones. Vas a ver por qué cada abstracción tiene un costo continuo y no de una sola vez; la regla de tres con sus matices y sus excepciones; por qué la abstracción prematura es el error más caro del oficio —más que la falta de abstracción, porque la que falta se agrega cuando duele, y la que sobra casi nunca se quita—; qué significa YAGNI en la práctica y dónde no aplica; el anti-patrón del plugin para una sola implementación, con plugins/ de Boletia como caso de estudio de principio a fin; el intercambio entre acoplamiento e indirección, que es el verdadero eje de todas estas decisiones; y un proyecto donde vas a quitar una abstracción y justificarlo.
Que ese módulo venga antes de estudiar un solo patrón concreto es la decisión de diseño más importante de esta guía. El orden inverso —catálogo primero, criterio después— es exactamente cómo se fabrica lo que acabamos de reconstruir en Boletia.
Errores comunes
Confundir la prudencia con no diseñar nunca (de criterio). Qué pasa: alguien lee una lección como esta, concluye que abstraer es peligroso, y adopta la posición contraria: nunca introduce estructura, deja crecer los condicionales, tolera la duplicación indefinidamente. A los dos años tiene un checkout de trescientas líneas con un elif por cada combinación. Por qué pasa: es la reacción de péndulo típica ante una advertencia fuerte, y se disfraza de pragmatismo. Cómo detectarlo: si en un año no introdujiste ninguna abstracción, no estás siendo prudente: estás usando una regla para no pensar, igual que quien abstrae todo. Cómo corregirlo: la posición correcta no es "no abstraigas" sino "abstrae cuando el problema se manifieste, y entonces hazlo bien". El módulo 2 se llama "cuándo NO abstraer" precisamente porque tiene un cuándo; no se llama "nunca abstraigas". Y el módulo 8 va a pedirte hacer las dos cosas sobre el mismo sistema: quitar donde sobra y agregar donde falta.
Usar "abstracción prematura" como comodín para rechazar cualquier propuesta (social). Qué pasa: alguien aprende el concepto y lo empieza a usar como respuesta automática en revisiones. Cualquier estructura propuesta recibe la etiqueta, sin analizar el caso. El equipo deja de proponer mejoras estructurales porque sabe cuál va a ser la respuesta. Por qué pasa: es la misma falla que la lección 3 llamó "el nombre como sustituto del argumento", solo que en dirección contraria: en vez de "esto es un Factory", ahora es "esto es abstracción prematura". Sigue siendo una etiqueta reemplazando un análisis. Cómo detectarlo: si tu rechazo no incluye el conteo de implementaciones reales ni una pregunta sobre qué cambio solicitado hay detrás, es una etiqueta. Cómo corregirlo: usa la prueba de las tres preguntas en voz alta. Si quien propone contesta "hay tres tipos hoy y marketing pidió dos más", la respuesta correcta ya no es "prematuro": es "de acuerdo, hablemos de la forma".
Creer que esta lección invalida el resto de la guía (de expectativa). Qué pasa: alguien lee siete lecciones sobre patrones y luego una que dice cómo no usarlos, y queda con la sensación de que todo el tema es una trampa. Por qué pasa: la lección es deliberadamente enfática, y el énfasis se puede confundir con negación. Cómo detectarlo: si tu conclusión es "entonces mejor no aprender patrones", leíste una advertencia sobre el método como una advertencia sobre el contenido. Cómo corregirlo: recuerda la promesa de la guía —vocabulario y criterio, las dos—. El vocabulario se usa todos los días para leer y para conversar, y no tiene ningún riesgo asociado: nombrar una estructura no cuesta nada. Lo que tiene riesgo es aplicar sin criterio, y por eso el criterio viene antes que la aplicación. Los módulos 3 a 6 sí enseñan a aplicar, y para entonces vas a tener con qué decidir.
Ejercicios
Ejercicio 1 — Aplica la prueba de las tres preguntas. Para cada propuesta, aplica las tres preguntas y da un veredicto: adelante, esperar, o "faltan datos y cuáles".
- En
reports/hay tres exportadores (CSV, PDF, XLSX) que repiten el mismo esqueleto de cuatro pasos. Alguien propone unificar el esqueleto con un Template Method. - Boletia usa un solo servicio de correo. Su SDK aparece importado en once archivos distintos. Alguien propone un Adapter.
- Hoy hay un tipo de cupón de descuento. Alguien propone una interfaz
Couponcon una implementación, "porque seguro vienen más". - El
checkoutavisa a cinco destinatarios llamándolos por nombre, y el equipo de fraude ya pidió por escrito ser el sexto. Alguien propone un Observer.
Ver solución
1. Adelante. (1) Tres implementaciones reales, hoy, en el repositorio. (2) El cambio concreto: cualquier ajuste al flujo general —agregar un filtro de cancelados, cambiar el orden— hoy exige revisar tres caminos y acordarse de los tres. Eso ya duele, no es pronóstico. (3) Se paga con un salto: leer una exportación completa deja de ser leer de corrido, y los tres quedan atados a un esqueleto común. Con tres variantes y un dolor ya manifiesto, sale a cuenta.
2. Adelante, aunque haya una sola implementación. Esta es la excepción importante. (1) Una sola implementación —lo que normalmente diría "espera"—. (2) Pero el cambio concreto no es "cambiar de proveedor": es que el idioma del SDK está en once archivos, así que cualquier cambio en su API nos toca en once lugares, y además esos once archivos no se pueden probar sin el SDK. Ese dolor es actual y verificable. (3) Se paga con una capa más y el riesgo de que el adaptador quede tan delgado que no traduzca nada. Veredicto: adelante, y fíjate en por qué —el problema aquí no es la variedad sino el contagio—. Es un buen ejemplo de que la regla de tres es una guía, no un dogma.
3. Esperar. (1) Una implementación. (2) La justificación —"seguro vienen más"— está en futuro y no cita ninguna petición. (3) Se paga con un archivo extra, un nombre extra y un salto, hoy, a cambio de nada. Veredicto: dejar el cupón directo. Cuando llegue el segundo tipo, un if; cuando llegue el tercero, la interfaz —y para entonces sabrás cómo tiene que ser, porque tendrás tres ejemplos en vez de uno imaginado—.
4. Adelante. (1) Cinco destinatarios reales y un sexto solicitado por escrito. (2) El cambio concreto: agregar el sexto obliga hoy a abrir el archivo más delicado del sistema para algo que no tiene nada que ver con cobrar; y un canal caído tumba un cobro que ya salió bien. Los dos dolores son actuales. (3) Se paga caro y hay que decirlo: el flujo deja de seguirse con el dedo y depurar "por qué no llegó el correo" se alarga. Aun así, con seis interesados sale a cuenta. Y conviene hacerlo en pasos pequeños —empezar con fraude y el organizador, dejar el resto— en lugar de reescribir el módulo.
Por qué funciona: los cuatro casos tienen respuestas distintas y ninguna salió de una regla mecánica. El caso 2 rompe la regla de tres con buen argumento, y el caso 4 acepta un costo alto porque el beneficio es mayor. Eso es criterio: la regla orienta, el caso decide.
Ejercicio 2 — Reescribe el razonamiento de plugins/. Vuelve al párrafo donde la persona de Boletia justifica la arquitectura de plugins. Reescríbelo como lo habría escrito alguien con los cuatro hábitos de esta lección: mismo problema —el teatro pidió asientos numerados—, mismo nivel técnico, pero con el freno puesto. Después escribe la línea que esa persona habría dejado en el código o en el ticket para no perder la idea.
Ver solución
Un razonamiento posible:
"El teatro necesita asignar asientos numerados y el criterio de 'mejor asiento' probablemente cambie según el tipo de recinto. Pero hoy tengo exactamente un recinto y un criterio, así que no sé cómo va a variar de verdad: si diseño la interfaz ahora, la voy a diseñar con una sola muestra y lo más probable es que no le sirva al segundo caso. Escribo
assign_seatcomo función directa. Cuando llegue el segundo recinto, unifcon dos criterios está bien y ahí voy a ver qué tienen en común. Con el tercero, saco la familia con la interfaz que los tres casos reales me hayan mostrado. Y lo de que terceros aporten implementaciones: nadie lo ha pedido; si algún día lo piden, será un proyecto con sus propios requisitos —autenticación, versionado, aislamiento— y ninguno de esos cabe en un registro dinámico improvisado hoy."
La línea para no perder la idea, en el ticket o como comentario:
# Archivo: seating.py
# Hoy hay un solo criterio de asignación (más cerca del escenario).
# Si aparece un segundo tipo de recinto con otro criterio, esto es candidato
# a familia de estrategias. No abstraer hasta tener al menos dos casos reales
# para ver qué tienen en común de verdad.
def assign_seat(event, order):
...
Fíjate en tres cosas de ese comentario. Primero, deja constancia del razonamiento para quien venga después, incluido tú mismo, que en seis meses no vas a recordar por qué no abstrajiste. Segundo, nombra el disparador —"un segundo tipo de recinto con otro criterio"— y no un plazo ni una vaguedad. Y tercero, no cuesta nada: es un comentario, no una arquitectura.
Por qué funciona: la diferencia entre las dos versiones no es el nivel técnico ni la capacidad de anticipar. Las dos personas anticiparon lo mismo. La diferencia es qué hicieron con la anticipación: una la convirtió en código, la otra en una nota. La nota se puede ignorar sin costo; el código hay que mantenerlo dos años.
Ejercicio 3 — Encuentra tu propio martillo. Piensa en la última cosa técnica que aprendiste con entusiasmo —un patrón, una librería, una técnica, un lenguaje, una forma de estructurar proyectos—. Responde: (a) ¿en qué lugar la aplicaste donde, mirándolo con calma, no hacía falta? (b) ¿qué se volvió más difícil por haberla aplicado ahí? (c) ¿qué señal te habría avisado en el momento?
Ver solución
No hay respuesta única, y este ejercicio no tiene una solución sino una advertencia sobre cómo responderlo.
La trampa es contestar "ninguno, siempre lo apliqué bien". Es posible, pero es poco frecuente, y vale la pena buscar con más ganas antes de darlo por bueno. El fenómeno del martillo nuevo no es un defecto de carácter: es cómo funciona el aprendizaje humano en general. Que te haya pasado no dice nada malo de ti; que creas que nunca te pasó probablemente signifique que todavía no volviste a leer ese código.
Sobre (b), la respuesta más común y más reveladora no es "quedó más lento" ni "tuve que escribir más": es "alguien más tardó en entenderlo" o "yo mismo tardé en entenderlo seis meses después". Ese es el costo característico de la complejidad innecesaria, y es el que no se ve el día que la introduces.
Sobre (c), las señales que la gente reporta suelen ser tres, y las tres son útiles como alarma personal:
- La sensación de estar buscando dónde usarlo en vez de encontrarte con el problema.
- Tener que explicar el diseño más de una vez a personas distintas. Si una estructura necesita explicación repetida, la estructura está cargando información que el código no comunica solo.
- Que la justificación esté en futuro. "Por si acaso", "cuando escalemos", "seguro vamos a necesitar". Esa forma verbal es la alarma más confiable que existe, y funciona igual de bien para auditar propuestas ajenas.
Por qué funciona: reconocer el fenómeno en algo que ya te pasó lo vuelve concreto. Y el objetivo de este ejercicio no es que te sientas mal por un código viejo: es que aprendas a reconocer la sensación —el entusiasmo buscando dónde aplicarse— porque es la única señal disponible en tiempo real, antes de que el código exista.
Resumen y siguiente paso
En esta lección vimos de frente cómo no estudiar patrones. Reconstruimos el nacimiento del rincón plugins/ de Boletia y encontramos que cada paso del razonamiento era plausible, que el costo real no fue escribir de más sino dos años de gente leyendo tres archivos y un editor que no puede navegar el vínculo, que el diseño quedó peor que si se hubiera esperado —porque con una sola muestra se abstrae mal—, que nadie lo quitó nunca, y que el problema no fue el patrón sino el orden: la solución llegó antes que el problema.
Tienes los cuatro anti-métodos: memorizar diagramas —la casilla que menos decide, con nombres genéricos que no se parecen a los del dominio—; salir a buscar dónde aplicar lo recién aprendido —el martillo nuevo, que siempre "encuentra" y por eso engaña—; contar patrones como puntos —midiendo la variable equivocada, empujado por una asimetría social real—; y estudiar la forma sin el costo, que es el que habilita a los otros tres, con su variante del ejemplo de juguete donde el patrón siempre vale la pena porque el ejemplo se construyó para eso.
Y tienes los cuatro hábitos que los reemplazan: parte del problema y busca el nombre al final; aprende el costo en la misma sesión que la forma; practica el "no" tanto como el "sí"; y deja que el patrón emerja del refactor. Más la prueba de las tres preguntas —cuántas implementaciones reales hay hoy, qué cambio concreto y solicitado se abarata, y qué se vuelve más difícil y quién lo paga— que puedes aplicar en un minuto antes de tocar cualquier código.
Antes de avanzar deberías poder: reconocer el martillo nuevo en ti mismo; explicar por qué abstraer temprano suele producir la abstracción equivocada y no solo una de más; y aplicar las tres preguntas a una propuesta concreta llegando a un veredicto que no sea automático.
Lo que viene es el proyecto del módulo, y está diseñado con esta lección adentro. Vas a recorrer Boletia completa y entregar un inventario de lo que reconoces: qué estructuras hay, cómo se llama cada una, qué problema resuelve ahí, y —parte obligatoria de la entrega— cuáles no logras nombrar, que es información valiosa y no un fracaso. Sin cambiar una sola línea de código. Ese inventario va a ser tu base de trabajo para los siete módulos restantes, empezando por el módulo 2, donde vas a tomar una de esas entradas y quitarla.
Recursos
- The Grug Brained Developer — el mejor texto que existe sobre la complejidad como enemigo, escrito como comedia. Es esta lección y el módulo 2 completos, en veinte minutos de lectura.
- YAGNI (Martin Fowler) — la entrada corta que explica por qué construir para necesidades presuntas cuesta más de lo que parece, incluyendo el costo de arrastrarlo.
- Premature Generalization / Speculative Generality (Fowler & Beck, en Refactoring) — el olor de código que describe exactamente el rincón
plugins/, con su nombre formal. - Simple Made Easy (Rich Hickey) — una charla sobre la diferencia entre simple y fácil, y por qué la complejidad que agregamos por comodidad se cobra después. Cambia cómo se ve cualquier decisión de diseño.