Módulo 7: Patrones como vocabulario de revisión

1. Presentación del módulo: nombrar es la mitad de arreglar

Descripción

Al terminar esta lección vas a tener tres cosas. Primero, vas a entender por qué este módulo —y no el catálogo de patrones que viste en los módulos 3 a 6— es donde esta guía cobra su promesa: el entregable real no es saber qué es un Decorator, es poder decir en una revisión de código lo que ves, con precisión y en pocas palabras. Segundo, vas a ver medida la diferencia entre un comentario de revisión vago y uno nombrado, y la unidad de medida no va a ser la elegancia: va a ser qué puede hacer el otro con él. Y tercero, vas a tener clara la frontera con clean-code-and-code-review-guide, que es la guía hermana con la que este módulo se toca más: allá está el proceso del review, aquí está el vocabulario que lo hace posible.

Esto importa por una razón que se nota el primer día que te toca revisar el código de alguien más. Vas a ver algo que te incomoda. Vas a saber, con bastante certeza, que ese código va a doler en tres meses. Y vas a descubrir que la distancia entre ver el problema y poder decirlo es enorme. Te va a pasar lo siguiente: escribes un párrafo largo tratando de explicar la estructura que te preocupa, lo relees, te suena a sermón, lo borras, escribes "creo que esto se podría simplificar", y aprietas Aprobar. El problema siguió ahí. No porque no lo vieras: porque no tenías las palabras para ponerlo sobre la mesa en un tamaño que cupiera en un comentario.

Los módulos anteriores te dieron la mitad del diccionario: los nombres de las estructuras que funcionan —Strategy, Factory, Adapter, Observer—. Este módulo te da la otra mitad, la que casi ningún curso enseña: los nombres de las estructuras que fallan. God object, feature envy, shotgun surgery, speculative generality. Con las dos mitades puedes decir en una línea tanto "esto pide una Strategy" como "esto es un God object", y en los dos casos la persona del otro lado ve la misma imagen que tú.

Conexión con el módulo: esta lección es el marco. Todavía no vas a diagnosticar nada; vas a entender qué es un diagnóstico y por qué el módulo está ordenado así. La lección 2 instala la idea central del módulo —qué es un olor y por qué un olor se investiga en vez de corregirse de reflejo— y te da el catálogo corto. La lección 3 se para con lupa sobre los tres olores más frecuentes y más caros, uno por uno, con su ejemplo en Boletia. La lección 4 es la lección técnica del módulo: la fórmula concreta para escribir un comentario accionable, con ejemplos antes y después. La lección 5 cubre los anti-patrones, que son el lado oscuro del catálogo que aprendiste en los módulos 3 a 6. La lección 6 enseña el movimiento en las dos direcciones: refactorizar hacia un patrón y —lo que casi nunca se enseña— hacia afuera de uno. La lección 7 pone la dimensión humana: cómo se ofrece un diagnóstico sin que suene a sentencia. Y la lección 8 —el proyecto— te entrega un pull request real sobre Boletia y te pide escribir la revisión completa.

Dos formas de decirle al médico que te duele

Piensa en dos personas que entran a urgencias la misma tarde.

La primera dice: "me duele la panza". Es verdad, es toda la información que tiene, y no está haciendo nada mal. El médico ahora tiene que empezar de cero: ¿desde cuándo?, ¿dónde exactamente?, ¿comiste algo raro?, ¿te duele si presiono aquí?, ¿y si suelto? Media hora de preguntas para llegar a una hipótesis.

La segunda dice: "dolor en el cuadrante inferior derecho, empezó cerca del ombligo hace ocho horas y migró, me duele más cuando sueltas que cuando presionas". En una frase entregó localización, evolución temporal y un signo clínico con nombre. El médico no es mejor médico por eso, pero ya está pensando en apendicitis y pidiendo estudios, no haciendo preguntas.

Fíjate bien en lo que cambió y en lo que no. El dolor es el mismo. La segunda persona no está más enferma ni tiene mejor pronóstico por saber la palabra "cuadrante". Lo que cambió es el costo de transmitir el problema y la precisión con la que llegó al otro lado. Y hay un efecto de segundo orden, más silencioso: con el vocabulario, la conversación puede avanzar. Se puede discutir si es apendicitis o no. Sin el vocabulario, la conversación se queda atascada en establecer los hechos básicos.

Eso es exactamente lo que pasa en una revisión de código. Tú ves algo. Si dices "esto está feo", el otro tiene que empezar de cero: ¿qué parte?, ¿feo cómo?, ¿qué te preocupa?, ¿qué esperabas ver? Si dices "esto es shotgun surgery: agregar un proveedor de pago toca cinco archivos", el otro ya está pensando en dónde concentrar la decisión, no en qué quisiste decir.

Y hay una tercera persona que conviene mencionar, porque también existe y hace daño: la que llega diciendo "tengo apendicitis". No dice lo que siente; dice el diagnóstico, y lo dice cerrado. Si acierta, ahorró tiempo. Si se equivoca, mandó al médico en la dirección incorrecta con toda confianza. En una revisión de código, esa tercera persona es la que escribe "esto es un anti-patrón" y aprieta Solicitar cambios sin explicar cuál ni por qué. La lección 7 se dedica entera a ese riesgo, porque es el precio de tener vocabulario: una palabra usada mal viaja igual de rápido que una usada bien.

Ejemplo trabajado: el mismo hallazgo, escrito de cuatro formas

Vamos a hacer esto concreto. Estás revisando un cambio en Boletia. Tu compañero agregó un cuarto tipo de reporte y, de paso, tocó el checkout. Al leer el diff te encuentras con esto:

# Archivo: checkout/checkout.py   (fragmento del diff)
# El compañero agregó las líneas marcadas con "+".

def checkout(order, coupon=None):
    tickets = [repository.get_ticket(tid) for tid in order.ticket_ids]

    # ---- 1. Precio -------------------------------------------------
    subtotal = 0.0
    for ticket in tickets:
        subtotal += calculate_price(ticket, order.created_at)
    fee = subtotal * SERVICE_FEE_RATE
    order.total = round(subtotal + fee, 2)

    # ---- 3. Cobro ---------------------------------------------------
    if order.provider == "stripe":
        provider = StripeProvider(StripeClient(api_key=settings.STRIPE_KEY))
    elif order.provider == "mercadopago":
        provider = MercadoPagoProvider(MercadoPagoClient(token=settings.MP_TOKEN))
    elif order.provider == "cash":
        provider = CashProvider()
+   elif order.provider == "klarpay":
+       provider = KlarpayProvider(KlarpayClient(key=settings.KLARPAY_KEY))
    else:
        raise ValueError(f"Proveedor desconocido: {order.provider}")

    result = provider.charge(order)
    ...

Y en otro archivo del mismo diff:

# Archivo: api/routes.py   (fragmento del diff)

- ALLOWED_PROVIDERS = ("stripe", "mercadopago", "cash")
+ ALLOWED_PROVIDERS = ("stripe", "mercadopago", "cash", "klarpay")

Y en un tercero:

# Archivo: config.py   (fragmento del diff)

class Settings:
    def _load_from_env(self):
        self.STRIPE_KEY = os.environ["STRIPE_KEY"]
        self.MP_TOKEN = os.environ["MP_TOKEN"]
+       self.KLARPAY_KEY = os.environ["KLARPAY_KEY"]

Tú notas algo. Este cambio, que conceptualmente es una sola idea —"Boletia ahora también cobra con Klarpay"—, quedó repartido en tres archivos, más el archivo nuevo del proveedor, más la prueba. Cinco lugares para una idea. Y el quinto proveedor va a repetir exactamente el mismo recorrido.

Veamos cuatro formas de decir eso, de peor a mejor.

Versión 1 — el comentario que no dice nada.

"Esto se podría mejorar 🙂"

Cero información. El autor no sabe qué te preocupa, ni dónde, ni si tiene que hacer algo. Lo más probable es que responda "¿en qué sentido?" y que la conversación consuma dos días de ida y vuelta, o que simplemente marque el comentario como resuelto sin cambiar nada. Este comentario tiene un costo neto negativo: gastó atención de dos personas y no movió el código.

Versión 2 — el párrafo bienintencionado.

"Oye, veo que para agregar este proveedor tuviste que tocar el checkout, el routes y el config. Me da la impresión de que cada vez que agreguemos una forma de cobrar va a pasar lo mismo, y el checkout es la parte más delicada del sistema. No sé, siento que sería mejor si toda la información de un proveedor viviera junta en un solo lado y el checkout no tuviera que enterarse. ¿Qué opinas?"

Esto ya es feedback de verdad: es correcto, es amable y el diagnóstico es exacto. Tiene dos problemas. El primero es de costo: te tomó cuatro minutos escribirlo y le va a tomar dos minutos leerlo, y en un PR de nueve comentarios eso no escala. El segundo es más sutil: le pasaste al otro la reconstrucción. Tú viste una estructura; se la entregaste en prosa, y él tiene que volver a armarla en su cabeza. La frase "siento que sería mejor si" también deja la observación en terreno de gustos, cuando no lo es.

Versión 3 — el nombre suelto.

"Esto es shotgun surgery."

Tres palabras, y el diagnóstico es correcto. Pero está incompleto de una forma que hace daño. Si el autor no conoce el término, el comentario es un muro: ahora tiene que ir a buscarlo, y mientras lo busca va a sentir —con razón— que le pusiste una etiqueta encima. Si sí lo conoce, todavía le falta lo importante: ¿dónde exactamente?, ¿esto es bloqueante o es una nota?, ¿qué se supone que haga? Un nombre sin dirección es un veredicto, y un veredicto cierra la conversación en vez de abrirla.

Versión 4 — el diagnóstico accionable.

checkout/checkout.py:41 — Esto es shotgun surgery: un cambio conceptual ("soportamos un proveedor más") obligó a tocar cinco lugares —este elif, la lista blanca de routes.py, la credencial en config.py, el archivo nuevo del proveedor y su prueba—. El quinto proveedor va a repetir el mismo recorrido, y el riesgo está en que se olvide uno: si el elif entra y la lista blanca no, la API rechaza órdenes que el checkout sí sabría cobrar.

Una dirección posible: mover la decisión a un registro en payments/ —un diccionario nombre → constructor que los tres módulos consulten— para que agregar un proveedor sea un archivo nuevo más una línea de registro. Eso es lo que en el módulo 4 llamamos Factory.

No bloqueante para este PR: el cambio como está funciona. Lo dejo como el trabajo que vale la pena hacer antes del quinto proveedor.

Qué esperar de esta comparación. Lo primero que hay que ver es lo que no cambió entre la versión 2 y la 4: el diagnóstico es el mismo. El vocabulario no te hizo ver nada que no vieras. Lo que hizo fue otra cosa, y son cuatro cosas distintas que conviene separar porque la lección 4 las va a convertir en una fórmula.

La versión 4 tiene un nombre (shotgun surgery), que comprime en dos palabras la estructura completa del problema y la vuelve buscable, discutible y comparable con otros casos. Tiene una ubicación exacta (checkout.py:41, y los otros cuatro lugares enumerados), que convierte una impresión en un hecho verificable: el autor puede abrir el archivo y contar. Tiene una consecuencia dicha en términos de trabajo futuro y de riesgo concreto —"si el elif entra y la lista blanca no, la API rechaza órdenes"—, no en términos de principios abstractos. Y tiene una dirección: una opción concreta, ofrecida como opción, con el nombre del patrón que la describe.

Fíjate también en la última línea, que es la que más gente olvida: el peso. "No bloqueante" le dice al autor qué hacer hoy. Sin esa línea, todo comentario largo se lee como una condición para aprobar, y el autor termina rediseñando el sistema en un PR que era de una funcionalidad.

Y ahora la comparación que de verdad importa: la versión 4 se lee en veinte segundos y te tomó menos escribirla que la versión 2. Ese es el punto de todo el módulo. El vocabulario no es adorno académico: es lo que hace que decir la verdad completa cueste menos que decir media verdad.

Una última observación sobre la versión 1, porque es la más común de las cuatro en equipos reales. Nadie escribe "esto se podría mejorar 🙂" por flojera. Se escribe cuando alguien ve el problema, siente que explicarlo va a costar cuatro minutos y una discusión, calcula que no vale la pena, y suelta algo tibio para no quedarse callado del todo. El vocabulario es lo que baja ese costo lo suficiente como para que valga la pena decirlo. Por eso este módulo no es sobre palabras: es sobre qué problemas terminan dichos y cuáles se quedan adentro.

Las dos mitades del diccionario

Los módulos 3 a 6 te dieron nombres para estructuras que resuelven problemas. Este módulo te da nombres para estructuras que causan problemas. Vale la pena ver las dos mitades juntas, porque casi siempre están emparejadas: cada olor apunta hacia uno o dos patrones que suelen resolverlo, y cada patrón mal aplicado produce un anti-patrón reconocible.

Lo que ves (el olor)Qué significa en una fraseHacia dónde suele resolverse
God objectUna clase o archivo que sabe y hace demasiado; todo el mundo lo tocaExtraer colaboradores: Strategy, Factory, o simplemente funciones
Feature envyUn método que usa más datos de otra clase que de la propiaMover el método a donde viven los datos
Shotgun surgeryUn cambio conceptual obliga a tocar muchos archivosConcentrar la decisión: Factory, registro, o un solo punto de configuración
Divergent changeUn archivo cambia por muchas razones distintas y sin relaciónPartirlo por razón de cambio
Switch repetido sobre un tipoEl mismo if sobre el mismo campo, contestado en varios archivosStrategy, o polimorfismo simple
Duplicated skeletonVarias clases con el mismo orden de pasos y un paso distintoTemplate Method
Primitive obsessionUn concepto del negocio viaja como str o int sueltoUn tipo propio
Message chaina.b.c.d.e — el código camina por la estructura de otrosPedir lo que necesitas, no navegar hasta ello
Speculative generalityEstructura de extensión para algo que nunca se extendióQuitarla (módulo 2, y lección 6 de este módulo)

La columna de la derecha es importante y también es una trampa, así que conviene decir lo dos cosas de una vez. Es importante porque un diagnóstico sin dirección es un veredicto, y en la lección 4 vas a ver que "una dirección posible" es parte obligatoria de un buen comentario. Es una trampa porque esa columna no es una tabla de conversión automática. Ver un if sobre ticket.kind no obliga a introducir una Strategy; obliga a preguntarse si vale la pena. El módulo 2 completo existe para esa pregunta, y la lección 2 de este módulo la va a formular en su forma más útil: un olor se investiga, no se corrige de reflejo.

Cómo está ordenado este módulo

Las siete lecciones que siguen van de tener las palabras a saber usarlas con otra persona. Ese orden no es casual.

LecciónQué instalaCon qué sales
2. Code smellsQué es un olor: una señal, no un error. La distinción que hace que investigues en vez de corregir de reflejo. El catálogo cortoReconocer y nombrar los olores que de verdad aparecen
3. God object, feature envy, shotgun surgeryLos tres más frecuentes y más caros, con lupa. Cómo se detectan de forma casi mecánicaDetectarlos en código que nunca viste, con evidencia contable
4. Nombrar para que sea accionableLa fórmula: nombre + ubicación + consecuencia + dirección + peso. Ejemplos antes/despuésEscribir un comentario sobre el que el autor pueda actuar sin volver a preguntar
5. Anti-patronesEl lado oscuro del catálogo: Singleton global, fábrica de fábricas, objeto ancla, herencia de cinco nivelesDistinguir un patrón legítimo de uno que se volvió el problema
6. Refactorizar hacia y hacia afueraQue el movimiento va en dos direcciones, y cómo se hace en pasos pequeños sin romper comportamientoProponer un camino, no solo un destino
7. Conversación, no sentenciaLa dimensión humana: ofrecer un diagnóstico como hipótesis, recibir uno sin defenderseUsar el vocabulario sin que se convierta en un arma
8. ProyectoUn pull request real sobre Boletia con varios problemas de estructuraLa revisión completa, escrita

Nota la forma. Las lecciones 2, 3 y 5 son vocabulario: te dan las palabras. La 4 y la 6 son técnica: qué haces con las palabras. La 7 es oficio: cómo se usan con una persona del otro lado que también tiene sentimientos y contexto. Y la 8 junta las tres capas.

Que la lección 7 venga al final es deliberado, y quiero explicar por qué. El riesgo de un módulo como este es fabricar lo que en la industria se conoce, sin cariño, como pattern police: alguien que aprendió doce nombres y los reparte por los pull requests como multas de tránsito. Ese perfil hace más daño que alguien sin vocabulario, porque cierra conversaciones con autoridad prestada. Las lecciones 2 a 6 te dan un arma; la 7 te enseña dónde apuntarla. Si solo lees una lección de este módulo, que sea la 4. Si lees dos, que la segunda sea la 7.

La frontera con clean-code-and-code-review-guide

Este módulo y esa guía se tocan más que ningún otro par del ecosistema, y por eso conviene ser quirúrgico con la división. La regla en una frase: allá está el proceso, aquí está el vocabulario.

PreguntaDónde se responde
¿Cómo se abre un buen pull request? ¿Qué tamaño debe tener? ¿Qué va en la descripción?clean-code-and-code-review-guide
¿Cuándo apruebo, cuándo pido cambios, cuándo solo comento? ¿Qué hago si el autor no está de acuerdo?clean-code-and-code-review-guide
¿Cómo se responde a un comentario sin que la conversación se descarrile? ¿Cuándo se mueve a una llamada?clean-code-and-code-review-guide (aquí lo tocamos en la lección 7, solo en lo que depende del vocabulario)
¿Cada cuánto se revisa? ¿Quién revisa qué? ¿Qué se automatiza con un linter y qué no?clean-code-and-code-review-guide
¿Cómo se llama esa estructura que me incomoda?Aquí
¿Qué consecuencia concreta tiene ese olor, y cómo la explico?Aquí
¿Esto que veo es un patrón legítimo o un anti-patrón?Aquí
¿Hacia qué patrón se resuelve este olor, y cómo se llega en pasos pequeños?Aquí (y el módulo 8 lo lleva hasta el final)
¿Cómo se escriben nombres de variables, funciones cortas, comentarios útiles?clean-code-and-code-review-guide

La forma mecánica de recordarlo: si la pregunta es sobre la conversación —cuándo, con quién, en qué tono, en qué momento del flujo—, es de la guía de al lado. Si la pregunta es sobre el contenido técnico de lo que vas a decir —cómo se llama, qué consecuencia tiene, hacia dónde va—, es de aquí.

Hay una zona de solapamiento honesto y es la lección 7. Ahí vamos a hablar de tono, de cómo ofrecer un diagnóstico en forma de pregunta y de cómo recibir uno sin defenderte. Podría parecer que invado. La distinción es esta: la lección 7 solo cubre los riesgos que nacen de tener vocabulario —etiquetar por etiquetar, usar un nombre como sentencia, equivocarse de nombre con confianza—. Todo lo demás sobre la etiqueta del review vive en la otra guía, y ahí es donde hay que ir a buscarlo.

Y un recordatorio de las otras dos fronteras, que ya viste en el módulo 1 y siguen vigentes. Los conceptos base —acoplamiento, cohesión, responsabilidad única— se dan por sabidos y vienen de software-development-foundations-guide; cuando digas "este método es feature envy" estás diciendo algo sobre cohesión, y asumo que la palabra ya significa algo para ti. Y la arquitectura a nivel sistema no es de aquí: un God object es una clase, no un microservicio.

Nombrar es la mitad, no el todo

El título de esta lección tiene una promesa y un límite, y quiero dejar los dos claros antes de avanzar.

La promesa es real. Un problema con nombre es un problema que se puede poner en una lista, priorizar, discutir en una reunión de equipo y comparar con otro. "El checkout está complicado" no entra a ningún backlog; "el checkout es un God object: cinco responsabilidades, lo tocaron trece de los últimos veinte PRs" sí entra, y además se puede medir si mejoró. El nombre convierte una molestia difusa en una unidad de trabajo.

Hay un efecto adicional que casi nadie menciona y que a mí, Mike, me parece el más valioso: nombrar despersonaliza. Cuando dices "esta función hace demasiado", hay un sujeto implícito —tú— con una opinión sobre algo que hizo otra persona. Cuando dices "esto es un God object", estás señalando una categoría conocida, que existía antes de ti y de él, y que le ha pasado a mucha gente. La conversación deja de ser "tú contra él" y pasa a ser "los dos contra una estructura conocida". Eso baja la temperatura de una forma que ningún ablandador retórico logra.

Ahora el límite, y es importante porque el resto del módulo depende de que lo tengas presente. Nombrar no arregla nada. Un equipo puede tener un vocabulario impecable y un código pésimo; de hecho, es una combinación bastante común, y tiene una forma reconocible: revisiones larguísimas, llenas de términos correctos, después de las cuales nadie cambia nada. Eso no es rigor, es teatro.

El nombre es la mitad porque hace tres cosas y ninguna más: vuelve el problema comunicable, lo vuelve comparable y lo vuelve priorizable. La otra mitad —decidir si vale la pena arreglarlo, en qué orden, con qué riesgo, y hacerlo en pasos que no rompan nada— empieza en la lección 6 de este módulo y ocupa el módulo 8 completo.

Y hay un caso que conviene tener en la cabeza desde ya, porque es el más maduro de todos: a veces el resultado correcto de nombrar un problema es dejarlo ahí. Escribes el comentario, el equipo entiende exactamente qué es, y la decisión colectiva es que hoy no vale la pena. Eso no es un fracaso del diagnóstico: es un diagnóstico que hizo su trabajo. La diferencia entre un olor ignorado y un olor decidido es enorme, aunque el código se vea igual.

Errores comunes

Creer que este módulo es sobre ser más duro en las revisiones (de expectativa). Qué pasa: alguien llega a un módulo titulado "code smells y anti-patrones" con la expectativa de salir con una lista de cosas que señalar, y su siguiente revisión pasa de tres comentarios a diecisiete. El autor del PR recibe una pared de texto, se desmoraliza, y el equipo empieza a evitar que esa persona sea la revisora. Por qué pasa: el vocabulario nuevo produce una sensación real de poder, y lo primero que uno quiere hacer con una herramienta nueva es usarla. Además, señalar más se siente como revisar mejor. Cómo detectarlo: si tus revisiones crecieron en número de comentarios pero el código no está mejorando, o si notas que la gente tarda más en pedirte revisión, ahí está la señal. Cómo corregirlo: la métrica de una buena revisión no es cuántos problemas señalaste, es cuántos se arreglaron. Tres comentarios accionables valen más que quince etiquetas. La lección 4 te da la fórmula y la lección 7 te da el criterio de cuándo callarte.

Usar el nombre como sustituto del argumento (conceptual). Qué pasa: alguien escribe "esto viola el principio de responsabilidad única" o "esto es un anti-patrón" y considera que ya explicó. El autor lee, no sabe qué se supone que va a doler ni cuándo, y la discusión se convierte en una pelea sobre si el principio aplica o no —que es una discusión abstracta que no termina nunca—. Por qué pasa: el nombre suena a autoridad, y hay un momento en la carrera de todo el mundo donde citar un principio se siente como ganar la discusión. También pasa por pereza honesta: articular la consecuencia concreta cuesta más que citar la regla. Cómo detectarlo: relee tu comentario y pregúntate si contiene al menos un hecho verificable —un número de archivos, un escenario de cambio, un caso que se rompería—. Si solo contiene nombres y principios, es una etiqueta. Cómo corregirlo: la regla de la lección 4 es que el nombre entra al comentario acompañado de su consecuencia, nunca solo. Un nombre te compra brevedad, no te exime del argumento.

Suponer que todo olor tiene que corregirse (de criterio). Qué pasa: alguien detecta correctamente un olor y salta directo a la corrección, normalmente introduciendo un patrón. Un if de dos ramas sobre ticket.kind se convierte en una jerarquía de clases; una función con cuatro parámetros se convierte en un Builder. El código resultante tiene el olor removido y un problema nuevo: indirección que nadie pidió. Por qué pasa: la palabra "olor" suena a defecto, y un defecto se arregla. Pero un olor no es un defecto: es una señal de que vale la pena mirar. A veces miras y el código está bien. Cómo detectarlo: si en los últimos tres refactors que hiciste ninguno terminó con la conclusión "está bien como está", probablemente estás corrigiendo de reflejo. Cómo corregirlo: la lección 2 desarrolla esto en serio. La pregunta de bolsillo mientras tanto: "¿qué me va a costar esto la próxima vez que alguien lo toque?". Si no puedes describir un escenario concreto de dolor, no hay caso que defender.

Ejercicios

Ejercicio 1 — Recupera un comentario que no escribiste. Piensa en la última vez que revisaste código de alguien más —o, si nunca lo has hecho formalmente, la última vez que leíste código ajeno y algo te incomodó—. Escribe: (a) qué viste exactamente, en una frase; (b) qué escribiste al final, si es que escribiste algo; (c) si no dijiste nada o dijiste algo tibio, cuál fue la razón honesta. Guarda esta nota: vas a volver a ella al terminar el módulo.

Ver solución

No hay respuesta única, pero sí un patrón que aparece en casi todas las respuestas y que vale la pena que veas escrito.

Sobre (c), las razones honestas suelen ser una de estas cuatro, y ninguna es pereza: "no sabía cómo decirlo sin sonar agresivo", "me iba a tomar mucho tiempo explicarlo", "no estaba seguro de tener razón", o "el PR ya llevaba tres días abierto y no quería frenarlo más".

Fíjate en lo que tienen en común las cuatro: todas son cálculos de costo. Nadie decide callarse porque el problema no importe; se calla porque el precio de decirlo —en tiempo, en fricción social o en riesgo de equivocarse— le pareció más alto que el beneficio. Y las cuatro tienen un antídoto distinto en este módulo. Para la primera, la lección 7. Para la segunda, la lección 4, que baja el costo de escribir de cuatro minutos a uno. Para la tercera, la lección 7 otra vez: la técnica de ofrecer el diagnóstico como pregunta te deja decir algo aun sin certeza. Para la cuarta, el peso del comentario: marcarlo como "no bloqueante" separa señalar un problema de frenar un PR.

Por qué funciona: el módulo no existe para que veas más problemas. Existe para que los que ya ves lleguen a decirse. Escribir hoy tu razón honesta te va a permitir medir, al final del módulo, si esa razón sigue en pie.

Ejercicio 2 — Clasifica seis preguntas entre esta guía y la de code review. Para cada una decide si la responde el módulo 7 de esta guía o clean-code-and-code-review-guide, y justifica en una línea. (a) "¿Cómo se llama cuando un método usa puros datos de otra clase?" (b) "Mi compañero no está de acuerdo con mi comentario y llevamos seis respuestas, ¿qué hago?" (c) "¿Este PR debería aprobarse aunque tenga un problema de estructura?" (d) "¿Qué consecuencia concreta tiene tener la lista de proveedores duplicada en dos archivos?" (e) "¿Qué tan grande debería ser un pull request?" (f) "¿Esta arquitectura de plugins es un patrón legítimo o es speculative generality?"

Ver solución

(a) Esta guía, lección 3. Es pura pregunta de vocabulario: se llama feature envy. Nombrarlo es exactamente lo que este módulo enseña.

(b) clean-code-and-code-review-guide. Es sobre el proceso y la dinámica de la conversación. La lección 7 de aquí te ayuda con una parte —cómo formular el diagnóstico para que no se atasque— pero qué hacer cuando ya se atascó, cuándo escalar y cuándo mover a una llamada es oficio de la otra guía.

(c) clean-code-and-code-review-guide. La decisión de aprobar o bloquear es política de equipo y proceso. Ahora bien, este módulo te da el insumo para tomarla: la lección 4 enseña a marcar el peso de cada comentario, y sin ese insumo la decisión de aprobar es a ciegas.

(d) Esta guía, lecciones 3 y 4. "Qué consecuencia concreta tiene" es el corazón de un diagnóstico accionable: en este caso, que las dos listas se desincronicen y la API rechace un proveedor que el checkout sí sabe cobrar.

(e) clean-code-and-code-review-guide. Tamaño de PR es mecánica del proceso, sin relación con patrones.

(f) Esta guía, lección 5, con raíz en el módulo 2. Distinguir un patrón legítimo de un anti-patrón es criterio de diseño, y es de las preguntas centrales de toda la guía.

Por qué funciona: la división no es burocrática. Si te llevas una pregunta al lugar equivocado vas a encontrar una respuesta parcial y vas a creer que es completa. Saber que "cómo se llama" y "cómo lo digo sin fricción" viven en guías distintas te ahorra frustración y, sobre todo, te ordena a dónde ir cuando falte una de las dos mitades.

Ejercicio 3 — Reescribe el comentario tibio. Aquí hay un comentario real del tipo que se escribe todos los días. Reescríbelo aplicando lo que viste en el ejemplo trabajado: nombre, ubicación, consecuencia, dirección y peso. El contexto es el mismo de la lección: reports/reconciliation.py es un archivo nuevo, de 140 líneas, cuya clase ReconciliationReport hace exactamente lo mismo que CsvExporter, PdfExporter y XlsxExporter —pedir los datos, ordenarlos, darles formato, escribir el archivo— y solo cambia en el paso del formato.

"Este archivo se parece mucho a los otros exportadores, ¿no?"

Ver solución

Una versión posible. No es la única correcta; lo que importa es que estén las cinco partes.

reports/reconciliation.py:12-140 — Este exportador repite el esqueleto completo de los otros tres (fetchsortformatwrite); lo único distinto es el paso del formato. Es duplicación de esqueleto, el olor que en el módulo 3 apuntaba a Template Method.

La consecuencia concreta: hoy son cuatro copias del mismo orden de pasos. Cuando cambie algo del esqueleto —por ejemplo, si hay que paginar la consulta de asistentes porque un evento grande revienta la memoria— hay que arreglarlo en los cuatro, y el que se olvide va a fallar solo en producción y solo con eventos grandes. Ya nos pasó con el sorted de PdfExporter, que quedó desalineado dos meses.

Una dirección posible: subir el esqueleto a una clase base con export() concreto y un _format(rows) abstracto, y dejar en cada exportador solo su paso. Son unas veinte líneas menos por archivo. Si prefieres no meterlo en este PR, con abrir un ticket me quedo tranquilo.

No bloqueante.

Compara con el original. "Se parece mucho a los otros exportadores, ¿no?" es una observación correcta pero deja tres huecos: no dice qué tiene de malo parecerse, no dice qué hacer, y su forma de pregunta retórica pone al autor a adivinar qué respuesta esperas —lo que suele terminar en un "sí, es verdad" y ningún cambio—.

Fíjate en un detalle de la versión larga: la frase "ya nos pasó con el sorted de PdfExporter". Un precedente real es el argumento más fuerte que existe en una revisión, porque convierte un riesgo hipotético en un hecho histórico del equipo. Cuando tengas uno, úsalo.

Por qué funciona: acabas de aplicar, sin haber visto todavía la fórmula, las cinco partes de un comentario accionable. La lección 4 las va a nombrar una por una y te va a dar la plantilla. Lo que quiero que notes ahora es que la versión larga no es más dura que la original —es más útil—, y que esas dos cosas no son lo mismo.

Resumen y siguiente paso

En esta lección viste que el entregable real de esta guía no es un catálogo memorizado, sino la capacidad de decir en una revisión lo que ves, con precisión y en pocas palabras. Viste la comparación de las cuatro versiones del mismo hallazgo —el comentario vacío, el párrafo bienintencionado, el nombre suelto y el diagnóstico accionable— y comprobaste que el mejor de los cuatro cuesta menos escribir que el segundo. Esa es la razón por la que el vocabulario importa: no porque suene profesional, sino porque baja el costo de decir la verdad completa, y ese costo es lo que decide qué problemas terminan dichos y cuáles se quedan adentro.

Viste que este módulo aporta la segunda mitad del diccionario —los nombres de las estructuras que fallan— y que cada olor apunta hacia uno o dos patrones que suelen resolverlo, con la advertencia de que esa tabla no es una conversión automática. Conociste el orden de las siete lecciones que vienen: vocabulario (2, 3, 5), técnica (4, 6), oficio (7) y proyecto (8). Y dejaste marcada la frontera con clean-code-and-code-review-guide: allá el proceso, la etiqueta y la mecánica del review; aquí el vocabulario que hace posible ese review.

Antes de avanzar deberías poder: explicar en una frase por qué "esto está feo" no es feedback; enumerar las cinco partes que tenía la versión 4 del comentario; decir en qué se diferencia un olor de un error; y saber a cuál de las dos guías llevar una pregunta sobre revisiones.

Lo que todavía no tenemos es la definición precisa de olor, y no es un detalle de vocabulario: es la distinción que separa a alguien que diagnostica de alguien que corrige de reflejo. Un olor no es un error —el código funciona—, y tampoco es una opinión —es observable y contable—. Es una señal de que algo probablemente esté mal estructurado, y la palabra "probablemente" carga todo el peso. La lección 2 desarma esa idea y te entrega el catálogo corto con el que vas a trabajar el resto del módulo.

Recursos

  • Refactoring: Improving the Design of Existing Code (Martin Fowler) — el libro que le puso nombre al catálogo de olores. El capítulo 3, "Bad Smells in Code", es la fuente de casi todo el vocabulario de este módulo.
  • Refactoring Guru — Code Smells — el catálogo de olores con ejemplos visuales y, para cada uno, las refactorizaciones que suelen aplicarse. Útil como diccionario de consulta durante una revisión.
  • Google Engineering Practices — How to do a code review — la guía interna de Google, publicada. Es la mejor referencia gratuita sobre el proceso del review; complementa este módulo por el lado que aquí no cubrimos.
  • Conventional Comments — una convención minimalista para marcar el peso de cada comentario (praise, nitpick, suggestion, issue, question, blocking). Encaja exactamente con la quinta parte de la fórmula que verás en la lección 4.