Módulo 8: Refactorizar con criterio (capstone)

7. Cuándo dejar el código feo en paz

Descripción

Al terminar esta lección vas a tener la habilidad más difícil de todo el módulo, que es no hacer nada a propósito. El código feo que funciona, que nadie toca y que nadie tiene que leer no es una prioridad, y decidir dejarlo así —con evidencia y por escrito— es una decisión de ingeniería tan real como cualquier refactorización. Vas a salir con las tres preguntas que deciden, con la forma de contestarlas con datos en vez de con impresiones, con la distinción entre feo y peligroso —que no son lo mismo y se confunden todo el tiempo—, y con el formato de la nota que deja constancia de que la decisión fue una decisión y no un olvido.

Esto importa por una razón aritmética. En cualquier sistema con más de tres años, la cantidad de código que podrías mejorar es varias veces mayor que la cantidad de tiempo que tienes. Eso no es un fracaso del equipo: es la condición normal del oficio. Si tu método para elegir es "arreglo lo que me incomoda cuando me lo encuentro", vas a gastar el presupuesto de refactorización en los rincones que casualmente te tocó abrir, que no tienen ninguna relación con los rincones que importan. Y vas a producir algo peor que perder el tiempo: cada cambio trae su propio riesgo, así que refactorizar lo que nadie toca introduce incidentes en un código que no los producía.

Y hay un componente que conviene decir sin adornos, porque es el que hace difícil esta lección: no tocar se siente como no trabajar. Un cambio de trescientas líneas se ve; una nota de cuatro líneas que dice "esto está feo, lo miramos y decidimos no tocarlo por estas tres razones" no se ve, aunque le ahorre al equipo dos semanas. Una parte de aprender criterio es aprender a estar cómodo con eso, y otra parte —más práctica— es aprender a escribir esa nota de forma que sí se vea.

Conexión con el módulo: las lecciones 3 y 4 te enseñaron a diagnosticar qué le pasa a un código; esta te enseña a decidir si hoy es el día, que es una pregunta distinta y posterior. La lección 5 te dio el costo real de ejecutar bien —pruebas, pasos, revisión— y ese costo es justamente lo que se pone en la balanza aquí. La lección 6 te dio la fórmula de la justificación, y la vas a usar igual: la decisión de no tocar se defiende con una unidad de medida, un costo y una condición, exactamente como la de tocar. Y el proyecto final te pide entregar, además del código, la lista de lo que decidiste no tocar y por qué; esta lección es la que la hace posible.

La bodega del fondo

Piensa en una casa que lleva veinte años habitada. Tiene un cuarto al fondo donde se guarda lo que no cabe en ningún otro lado: cajas sin etiquetar, una bicicleta con una llanta ponchada, el árbol de navidad, cables de aparatos que ya no existen. Cada vez que alguien abre esa puerta piensa lo mismo: "algún día hay que ordenar esto".

Y no lo ordenan. Pasan los años.

La pregunta interesante no es si esa bodega está desordenada —lo está— sino si ordenarla es lo que más rinde este sábado. Porque el sábado tiene ocho horas y hay otras cosas: la llave del baño que gotea, la instalación eléctrica de la cocina que salta cuando se enciende el horno y el microondas a la vez, el techo que hay que revisar antes de la temporada de lluvias.

Compara los cuatro problemas con tres preguntas:

¿Se usa seguido?¿Bloquea algo?¿Alguien tiene que entenderlo pronto?
La bodegano, se abre dos veces al añonono
La llave que goteasí, todos los díasno, pero cuesta agua cada díano
El eléctrico de la cocinasí: no puedes usar dos aparatossí, si viene un electricista
El techono se tocano, todavíano

La bodega es la única que contesta "no" a las tres, y es exactamente por eso que lleva veinte años igual. No es negligencia: es priorización correcta que nadie escribió. El problema no es que no la ordenen; el problema es que cada vez que alguien abre la puerta vuelve a evaluarlo desde cero y vuelve a sentir culpa. Si hubiera una nota pegada en la puerta que dijera "revisado en 2024: no vale la pena hasta que se necesite el cuarto", esa culpa desaparecería y la decisión quedaría tomada.

El techo, en cambio, contesta "no" a las tres hoy y su respuesta va a cambiar sola con la temporada de lluvias. Esa es la diferencia entre una nota con fecha y una nota con disparador, y la vamos a ver más adelante.

Y ahora la parte incómoda de la analogía, la que casi nunca se dice: ordenar la bodega no es gratis ni es neutro. Se rompe algo. Se tira algo que hacía falta. Aparece un cable que resulta que sí era de algo. Toda intervención tiene su propio riesgo, y en un cuarto que nadie abre, ese riesgo no se compensa con nada.

Feo no es lo mismo que peligroso

La confusión que más daño hace en esta lección es tratar "feo" y "peligroso" como sinónimos. Son dos ejes distintos, y el cuadro que forman decide qué hacer:

CorrectoCon un defecto real
BonitoNada que hacerArréglalo. Es un bug, no un refactor. Va con prioridad de bug
FeoAquí vive esta lección. Se decide con las tres preguntasArréglalo primero, y no lo mezcles con limpiar

Tres cosas de este cuadro.

La columna derecha no es tema de esta guía. Si un código tiene un defecto —cobra mal, pierde datos, deja pasar algo que no debería—, eso es un error y se arregla porque está mal, no porque esté feo. Confundir las dos cosas produce el peor de los mundos: un "refactor" que en realidad corrige un defecto, entregado sin la urgencia que un defecto merece y mezclado con cambios estructurales que hacen imposible revisar la corrección.

"Feo y correcto" es la casilla más grande de cualquier sistema real. Nombres malos, funciones largas, una duplicación evidente, un condicional anidado cuatro niveles. Funciona, lleva años funcionando, y no hay ninguna razón intrínseca para tocarlo.

Y la fealdad tiene un costo real, pero es un costo diferido y condicional. No cuesta nada mientras nadie lo lea; cuesta mucho el día que alguien tenga que cambiarlo. Por eso las tres preguntas de esta lección son todas sobre contacto humano futuro con ese código, y no sobre sus propiedades internas.

Las tres preguntas

Se contestan en este orden y cada una se contesta con evidencia, no con la sensación que te dejó leer el archivo.

Pregunta 1 — ¿Se toca seguido?

Cómo se contesta. Con el historial, no con la memoria:

# Cuántos cambios en el último año, y de qué tipo
git log --oneline --since="1 year ago" -- boletia/utils/dates.py

# Y la versión que más sirve: los archivos que más se tocan del proyecto
git log --since="1 year ago" --name-only --pretty=format: \
  | sort | uniq -c | sort -rn | head -20

Ese segundo comando es de lo más rentable que puedes correr en un proyecto nuevo. Te da, en diez segundos, la lista de los archivos que el equipo abre de verdad. Casi siempre hay una sorpresa: algún archivo que nadie considera importante está en el top cinco, y algún rincón que todo el mundo señala como problemático no aparece en toda la lista.

Por qué esta pregunta va primera. Porque el costo de la fealdad se paga cada vez que alguien la lee o la cambia. Un archivo tocado treinta veces al año paga ese costo treinta veces; uno tocado cero veces no lo paga nunca. La frecuencia de cambio es, con diferencia, el mejor predictor de dónde rinde una refactorización.

El matiz importante. No cuenta solo lo que se tocó: cuenta lo que se va a tocar. Si el archivo lleva dos años quieto pero el próximo trimestre entero depende de agregarle cosas, la respuesta es "sí" aunque el historial diga que no. El historial es evidencia del pasado y hay que combinarlo con el plan.

Pregunta 2 — ¿Bloquea algo?

Cómo se contesta. Con un trabajo concreto que hoy no se puede hacer, o que cuesta el doble por culpa de ese código. No con "nos frena en general".

Formas típicas de bloqueo real:

  • Una funcionalidad pedida que exige tocar ese rincón y que nadie quiere tomar. En Boletia: "necesitamos asignación de asientos por grupos y nadie quiere meterse en plugins/".
  • Un tipo de error que se repite por culpa de la estructura. En Boletia: la conciliación que se olvidó al agregar un proveedor, porque la lista está escrita en cuatro lugares.
  • Una prueba que no se puede escribir, y por eso hay un comportamiento sin cubrir.
  • Un tiempo de trabajo medible: "los dos últimos proveedores tomaron dos días y medio cada uno, y la mitad de ese tiempo fue encontrar todos los sitios".

Por qué esta pregunta es la más fuerte de las tres. Porque convierte tu propuesta en una decisión de negocio en vez de en una preferencia técnica. Y hay algo más, que conviene tener claro desde temprano en la carrera: la deuda de diseño casi nunca se paga porque alguien decida pagarla; se paga cuando bloquea algo que sí importa. Ese es el mejor momento para proponerlo, y no es una derrota: es que ahí es cuando de verdad rinde.

Pregunta 3 — ¿Alguien tiene que entenderlo pronto?

Cómo se contesta. Mirando quién va a leer ese código en los próximos meses:

  • ¿Entra gente nueva al equipo y este rincón está en su camino?
  • ¿La persona que lo escribió sigue en el equipo? Si se fue, y nadie más lo entiende, el riesgo sube aunque el archivo no se toque.
  • ¿Hay algo en el plan que obligue a leerlo —una auditoría, una migración, un cambio de proveedor?

Por qué existe esta pregunta aparte de las otras dos. Porque hay código que no se cambia pero sí se lee: el que hay que entender para saber si un problema viene de ahí. Un rincón de cálculo de dinero, por feo que sea, se lee cada vez que un cliente reclama. Ese contacto no aparece en el historial de commits y se olvida al contestar la pregunta 1.

La regla

Si las tres respuestas son "no", déjalo y anótalo. Si alguna es "sí", el trabajo entra a la cola —no necesariamente en primer lugar, pero entra— y se justifica con esa respuesta, que es tu unidad de medida.

Y una advertencia sobre el orden de las palabras: "déjalo y anótalo", no "déjalo". La nota es la mitad de la decisión. Sin ella, dentro de seis meses alguien va a abrir el mismo archivo, va a sentir la misma incomodidad y va a repetir el mismo análisis desde cero —o peor, va a decidir tocarlo sin hacerlo—.

Ejemplo trabajado: dos rincones feos de Boletia, una decisión distinta

Llegas a Boletia y en tu primera semana identificas dos rincones que te incomodan. Los dos son feos de verdad. Vamos a decidir sobre los dos con el mismo método.

Rincón A — utils/dates.py.

# Archivo: utils/dates.py   (fragmento de 140 líneas)

def _fix(dt, tz=None):
    """Normaliza."""
    if isinstance(dt, str):
        if len(dt) == 10:
            dt = datetime.strptime(dt, "%Y-%m-%d")
        elif dt.endswith("Z"):
            dt = datetime.strptime(dt[:-1], "%Y-%m-%dT%H:%M:%S")
        elif "+" in dt[10:]:
            base, off = dt[:19], dt[19:]
            dt = datetime.strptime(base, "%Y-%m-%dT%H:%M:%S")
            h, m = int(off[1:3]), int(off[4:6])
            delta = timedelta(hours=h, minutes=m)
            dt = dt - delta if off[0] == "+" else dt + delta
        else:
            dt = datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=timezone.utc)
    return dt.astimezone(tz or timezone.utc)

Es feo sin discusión: un nombre que no dice nada, un parseo a mano de cuatro formatos distintos, aritmética de zonas horarias escrita con las manos, y un docstring de una palabra. Cualquiera que lo lea siente el impulso de reescribirlo con una librería.

Rincón B — el bloque de precios del checkout, el if por tipo de boleto que ya conoces de la lección 3.

Ahora las tres preguntas, contestadas con datos:

utils/dates.pybloque de precios del checkout
¿Se toca seguido?2 commits en 3 años: un import y una compatibilidad al subir de versión de Python5 commits en 1 año, todos de negocio: descuento grupal, cortesías, cambio de VIP, tope de cortesías, corte de early-bird
¿Bloquea algo?No. Ningún trabajo pendiente lo tocaSí. Marketing pidió abonos de temporada y el trabajo está parado porque "hay que encontrar todos los if de kind"
¿Alguien tiene que entenderlo pronto?No. Nadie lo lee: se usa a través de parse_date() y funcionaSí. Es dinero: cada reclamo de un cliente por un cobro obliga a leerlo, y entran dos personas al equipo este trimestre
Incidentes atribuibles0 en 3 años3 cobros mal calculados por olvidar una rama al agregar un tipo de boleto
DecisiónDéjalo y anótaloRefactorízalo, y es el trabajo de más prioridad de los dos

Qué esperar de esta comparación. Cinco observaciones, y la segunda es la que más cuesta aceptar.

Primera: la fealdad no fue un criterio. Si tuvieras que ordenar los dos rincones por "qué tan feo se ve", utils/dates.py gana por mucho: el bloque de precios es largo pero perfectamente legible. Y la decisión salió al revés. La fealdad es lo que llama tu atención; no es lo que decide.

Segunda: no tocar utils/dates.py no significa que esté bien. Ese código tiene, casi seguro, un error latente con el horario de verano y otro con las fechas sin hora. Y aun así la decisión correcta hoy es dejarlo, porque no se ejecuta ninguna ruta donde eso importe y nadie lo va a tocar. Si mañana Boletia vende eventos en tres husos horarios, la respuesta cambia y cambia rápido. Esa es la diferencia entre "está bien" y "no es prioridad", y confundirlas es lo que hace que esta lección se lea como una excusa cuando no lo es.

Tercera: la pregunta 2 fue la que decidió. "Marketing pidió abonos y el trabajo está parado" es un hecho comprobable, con nombre y con fecha, y es la frase con la que se justifica el trabajo ante alguien que no lee código. Compárala con "el checkout está mal diseñado", que es cierta y no mueve a nadie.

Cuarta: los incidentes son la evidencia más contundente y casi nadie la busca. Tres cobros mal calculados no es una opinión sobre la estructura: es dinero real, con clientes reales, ya perdido. Esa fila del cuadro se llena buscando en el registro de incidentes por el nombre del módulo, y toma cinco minutos.

Y quinta: la decisión sobre utils/dates.py no termina en "no". Termina en una nota:

# Archivo: utils/dates.py   (cabecera agregada)

"""Parseo de fechas y zonas horarias.

REVISADO (jul-2026, en el trabajo de refactorización del checkout).
Este módulo está mal escrito: parsea cuatro formatos a mano, calcula
desplazamientos horarios en vez de usar una librería, y `_fix` no tiene
un nombre que signifique nada. Decidimos NO tocarlo por ahora:
  - 2 commits en 3 años, ninguno por un error.
  - 0 incidentes atribuibles.
  - Ningún trabajo pendiente lo toca; nadie lo lee (se usa vía parse_date).

SE VUELVE PRIORIDAD SI ocurre cualquiera de estas tres:
  1. Boletia vende eventos en más de un huso horario (hoy todo es America/Mexico_City).
  2. Aparece un incidente de fechas, por chico que sea.
  3. Alguien tiene que agregarle un formato nuevo.
En cualquiera de los tres casos, el plan es reemplazar el parseo a mano por
`datetime.fromisoformat` + `zoneinfo`, con pruebas de caracterización antes.
"""

Léela con atención, porque esa nota es el entregable de esta lección. Hace cuatro cosas: documenta que se miró —así nadie repite el análisis—; da la evidencia, con números; nombra tres disparadores concretos, no una fecha vaga; y deja el plan escrito para quien lo tome, que puede ser alguien que llegue dentro de un año. Cuesta cinco minutos y es lo más rentable que puedes dejar en un rincón que decides no tocar.

El costo real de refactorizar

Para que la balanza sea honesta hay que ponerle peso al otro plato, y ese peso casi nunca se cuenta completo. Refactorizar cuesta cinco cosas:

Uno: tu tiempo. El obvio, y normalmente el menor de los cinco. Un refactor mediano son dos o tres días, contando las pruebas que hay que escribir primero.

Dos: el tiempo de quien revisa. Un cambio estructural en un rincón delicado exige una revisión real, que puede ser media jornada de otra persona. Ese costo es invisible en tu planificación y muy visible en la de tu equipo. Y hay un efecto acumulativo: si mandas refactorizaciones seguidas de rincones que nadie toca, la próxima revisión que pidas —la que sí importa— va a recibir menos atención.

Tres: el riesgo. Todo cambio puede introducir un defecto, incluido uno hecho con pruebas y en pasos pequeños. Un cambio que no se hace tiene exactamente cero probabilidad de romper algo. Ese es el argumento más fuerte a favor de no tocar código que nadie toca, y es puramente aritmético: estás cambiando una probabilidad de cero por una probabilidad pequeña, a cambio de un beneficio que solo se cobra si alguien lee ese código, cosa que no ocurre.

Cuatro: el costo de oportunidad. Esos dos o tres días existen y se los estás quitando a otra cosa. La pregunta honesta no es "¿vale la pena refactorizar esto?" sino "¿vale más que lo otro que podría hacer con los mismos tres días?".

Cinco, y este es el que nadie cuenta: el conocimiento que se pierde. Un código feo con años encima contiene comportamiento intencional que no está documentado —las cercas de la lección 2—. Cada refactorización tiene una probabilidad real de borrar una de esas cercas sin que nadie se entere hasta meses después. Esa probabilidad baja mucho con pruebas de caracterización y una investigación previa, pero no llega a cero, y es mayor cuanto más viejo y menos documentado sea el rincón. Es decir: es mayor exactamente en el tipo de código que más ganas dan de reescribir.

Junta los cinco y la conclusión es incómoda pero clara: refactorizar es una inversión, y como toda inversión, tiene que rendir más que la alternativa. En un rincón que nadie toca, el rendimiento es cero por definición, porque el beneficio de un código legible solo se cobra cuando alguien lo lee.

La nota, y cómo se hace visible

La parte social importa. Una nota en el código soluciona el problema de la próxima persona que abra el archivo, pero no soluciona el de tu equipo, que no sabe que la miraste. Dos hábitos que arreglan eso:

Un registro de deuda revisado, no una lista infinita. Un archivo DEUDA.md o unos tickets etiquetados, con una regla: cada entrada tiene sus tres respuestas y su disparador. Y una segunda regla que es la que hace que funcione: se revisa cada trimestre y se borra lo que dejó de importar. Una lista de deuda que solo crece deja de leerse a los seis meses y se vuelve un cementerio.

Y menciona en tu entrega lo que decidiste no tocar. Cuando entregues el refactor del checkout, incluye la lista corta:

Lo que encontré y no toqué. utils/dates.py (140 líneas, parseo a mano, 2 commits en 3 años, 0 incidentes, nadie lo lee: nota y disparadores en la cabecera del archivo). El except Exception: pass de analítica en el checkout (es una cerca del #1904: analítica caída no debe tumbar una venta; solo le agregué el registro del fallo, que antes se tragaba en silencio). Los tres if de kind en el reporte de ventas por tipo (se van solos cuando Ticket.kind sea un enum; ese trabajo está en la cola y hay que hacerlo en un cambio propio porque toca datos).

Esa sección hace algo que su tamaño no sugiere: demuestra que revisaste el perímetro completo y elegiste, en vez de haber tocado lo primero que te incomodó. En una revisión, es la sección que más confianza genera en todo el documento, y es la que más rápido se escribe.

Errores comunes

Refactorizar por incomodidad estética (de criterio). Qué pasa: alguien abre un archivo para un cambio pequeño, ve un nombre malo o una función de ochenta líneas, y se pone a arreglarlo. El cambio de dos líneas se convierte en un diff de doscientas, la revisión se complica, y el trabajo que de verdad hacía falta llega dos días tarde. Y si algo falla, no se sabe si fue el cambio pedido o la limpieza. Por qué pasa: la incomodidad es inmediata y la prioridad es abstracta; además, arreglar lo que se ve mal produce una satisfacción real y rápida. Cómo detectarlo: pregúntate si estás tocando ese código porque lo tenías planeado o porque casualmente lo abriste. Si es lo segundo, sospecha. Cómo corregirlo: separa siempre —el cambio pedido en un commit, la limpieza en otro, y la limpieza solo si el archivo pasa las tres preguntas—. Y una regla de bolsillo: antes de empezar a arreglar un archivo, mira cuándo fue la última vez que alguien lo tocó. Si dice hace dos años, cierra el archivo.

El "regla del boy scout" mal entendido (de método). Qué pasa: existe un consejo clásico y bueno —"deja el código un poco mejor de como lo encontraste"— y alguien lo aplica sin límite: cada vez que pasa por un archivo lo reordena un poco. En rincones tranquilos eso es inofensivo. En el camino crítico del sistema, produce un goteo constante de cambios chicos, sin justificación individual, que nadie revisa a fondo porque cada uno parece trivial. Y el riesgo acumulado de veinte cambios triviales sin revisar a fondo no es trivial. Por qué pasa: el consejo se enuncia sin su condición, que es "mientras estés ahí por otra razón y el cambio sea del tamaño de lo que ya estás tocando". Cómo detectarlo: si tu "limpieza de paso" no cabe en la misma pantalla que el cambio que viniste a hacer, ya no es de paso. Cómo corregirlo: aplícalo a lo que tocas —el nombre de la variable que estás modificando, el comentario de la línea que cambiaste— y no al archivo entero. Y en el corazón del sistema, aplícalo con más cuidado todavía: el checkout no es lugar para mejoras espontáneas.

Confundir "no es prioridad" con "está bien" (de rigor). Qué pasa: alguien aplica correctamente las tres preguntas, decide no tocar un rincón, y con el tiempo esa decisión se convierte en la creencia de que ese código no tiene problemas. Cuando el disparador ocurre —Boletia empieza a vender en otro huso horario— nadie se acuerda de que había una decisión condicionada, y el problema aparece como sorpresa en producción. Por qué pasa: las decisiones sin fecha ni disparador escrito se convierten en estado permanente; es más fácil recordar "decidimos que estaba bien" que "decidimos que no era prioridad todavía". Cómo detectarlo: si tu nota dice "revisado, se deja" y no nombra ninguna condición que la reabra, tienes una decisión permanente disfrazada de temporal. Cómo corregirlo: toda nota de "no tocar" lleva disparadores concretos, y los disparadores se escriben como eventos observables —"si aparece un incidente de fechas", "si vendemos en un segundo huso horario"— y no como fechas —"revisar en seis meses"—, porque las fechas se cumplen sin que nadie las mire y los eventos, cuando ocurren, obligan a alguien a abrir el archivo.

Ejercicios

Ejercicio 1 — Aplica las tres preguntas. Para cada rincón, contesta las tres, di la decisión y —si la decisión es "déjalo"— escribe el disparador que lo reabriría. Inventa la evidencia que haga falta, pero márcala como algo que tendrías que ir a comprobar.

(a) Un módulo de exportación a un formato heredado que un solo cliente grande usa una vez al mes. Escrito hace cuatro años, dos commits, ilegible. El cliente renovó contrato por dos años. (b) Una función de cuarenta líneas en el checkout que arma el correo de confirmación, con HTML pegado a mano. Marketing pide cambios de texto cada dos meses. (c) Un script de migración de 2021 que ya se ejecutó y quedó en el repositorio. (d) La capa OrderService de la lección 4, con cinco métodos que solo reenvían. Se usa en tres archivos y no ha cambiado en dos años. (e) Un módulo de cálculo de comisiones para organizadores: feo, largo, sin pruebas. La persona que lo escribió se fue el mes pasado y contabilidad pregunta seguido por sus números.

Ver solución

(a) ¿Se toca seguido? No (2 commits en 4 años). ¿Bloquea? No. ¿Alguien tiene que entenderlo pronto? No, mientras siga funcionando. → Déjalo y anótalo. Disparador: "si el cliente pide un cambio de formato, o si el proceso falla una vez". Y una acción barata que sí conviene: comprobar que hay una alerta si la exportación mensual falla, porque un proceso que corre una vez al mes puede estar roto veintinueve días sin que nadie se entere. A comprobar: si existe esa alerta.

(b) ¿Se toca seguido? Sí, cada dos meses. ¿Bloquea? Parcialmente: cada cambio de texto obliga a tocar el checkout, que es el archivo más delicado. ¿Alguien tiene que entenderlo? Sí, quien haga esos cambios. → Refactorízalo, y con una forma concreta: sacar el texto a una plantilla para que un cambio de redacción no toque código del checkout. Nota que el objetivo no es que quede bonito: es que marketing deje de pasar por el corazón del sistema seis veces al año.

(c) Las tres: no.Déjalo… o bórralo, que es distinto. Aquí la respuesta interesante no es refactorizar sino eliminar: un script que ya corrió y no volverá a correr es código muerto, y el código muerto tiene un costo propio —aparece en las búsquedas, confunde a quien entra, y alguien puede ejecutarlo por error—. Borrar es más barato y más seguro que mejorar. A comprobar: que de verdad se ejecutó en todos los entornos.

(d) ¿Se toca seguido? No. ¿Bloquea? No. ¿Alguien tiene que entenderlo? Solo quien pase por ahí, y son tres archivos. → Déjalo y anótalo, aunque el diagnóstico de la lección 4 diga que sobra. Y este caso es el más importante del ejercicio: tener razón en el diagnóstico no obliga a actuar. Ahora bien, hay un momento en que sale casi gratis: si vas a tocar uno de esos tres archivos por otra razón, quitar el reenvío de paso cuesta dos líneas. Disparador: "la próxima vez que alguien toque uno de sus tres usuarios".

(e) ¿Se toca seguido? Probablemente no. ¿Bloquea? Hoy no. ¿Alguien tiene que entenderlo pronto? Sí, y con fuerza: contabilidad pregunta por sus números y la única persona que lo entendía se fue. → Actúa, pero fíjate en qué: la prioridad no es refactorizarlo. Es entenderlo y cubrirlo con pruebas que documenten qué calcula, porque el riesgo aquí no es la fealdad sino que nadie sepa si está bien. Es un caso donde la pregunta 3 sola decide, y donde la respuesta correcta es la lección 2 —leer e investigar— antes que la 3 o la 4.

Por qué funciona: de los cinco, dos se dejan, uno se borra, uno se refactoriza con un objetivo preciso y uno pide una acción que no es refactorizar. Esa variedad es el punto: las tres preguntas no producen un sí/no, producen qué hacer, y muchas veces lo que hay que hacer no es tocar la estructura.

Ejercicio 2 — Escribe la nota. Elige el caso (a) del ejercicio anterior —el exportador heredado— y escribe la nota completa que dejarías en la cabecera del archivo. Tiene que tener las cuatro partes: qué se revisó, la evidencia con números, los disparadores como eventos observables, y el plan para quien lo tome.

Ver solución

Una versión que funciona:

"""Exportación al formato heredado de Grupo Auditorio (contrato vigente hasta 2028).

REVISADO (jul-2026). El código es difícil de seguir: arma el archivo con
concatenación de cadenas, tiene el ancho de cada columna escrito a mano y
no está cubierto por ninguna prueba. Decidimos NO tocarlo:
  - 2 commits en 4 años, ninguno por un error.
  - Corre 1 vez al mes, para 1 cliente. 0 incidentes reportados.
  - Ningún trabajo pendiente lo toca y nadie más lee este módulo.

SE VUELVE PRIORIDAD SI:
  1. El cliente pide cualquier cambio de formato.
  2. El proceso mensual falla una vez.
  3. Se suma un segundo cliente con este formato.

PLAN cuando ocurra: capturar una salida real como archivo de referencia,
escribir una prueba que compare contra ella (prueba de caracterización), y
recién entonces reescribir la generación. Sin ese archivo de referencia no
hay forma de saber si el formato sigue siendo válido para el cliente.

PENDIENTE INDEPENDIENTE (barato, hacerlo ya): comprobar que existe alerta si
la ejecución mensual falla. Hoy podría estar rota 29 días sin que nadie lo note.
"""

Tres decisiones de esta nota que vale la pena señalar:

Los disparadores son eventos, no fechas. "Si el cliente pide un cambio" ocurre y obliga a alguien a abrir el archivo, momento en el que la nota se lee. "Revisar en enero" no se lee nunca.

El plan menciona el archivo de referencia. Esa es la parte técnica que salva a quien tome el trabajo dentro de dos años: en un formato heredado, la especificación real no está escrita en ningún lado, está en la salida que el cliente acepta. Quien no lo sepa va a reescribir el generador "limpio" y va a romper el formato.

Y hay un pendiente separado, barato y ya. La alerta de fallo no es refactorización: es un riesgo real que cuesta veinte minutos y no depende de la decisión de tocar el código. Distinguir "esto no es prioridad" de "aquí hay un riesgo barato de cubrir" es exactamente el tipo de precisión que esta lección busca.

Por qué funciona: la nota completa toma cinco minutos y ahorra dos cosas —que alguien repita el análisis, y que alguien reescriba el módulo sin saber que el formato no está documentado en ninguna parte—. Es el mejor rendimiento por minuto invertido de todo el módulo.

Ejercicio 3 — La decisión que cambia con el contexto. Toma el rincón plugins/ de Boletia, que sabes que sobra: 183 líneas, una implementación, cero agregadas en dos años. Aplica las tres preguntas en tres escenarios distintos y di qué harías en cada uno.

  • Escenario 1: nadie ha pedido nada relacionado con asientos en dos años. El rincón está quieto.
  • Escenario 2: el que conoces del proyecto: hay que agregar asignación por grupos y nadie quiere meterse ahí.
  • Escenario 3: hay un contrato firmado con un recinto grande que va a conectar su propio sistema de butacas dentro de cuatro meses, desplegado por ellos.
Ver solución

Escenario 1 — déjalo y anótalo. ¿Se toca? No (3 commits de mantenimiento en 2 años). ¿Bloquea? No. ¿Alguien tiene que entenderlo? No, salvo que se rompa. El diagnóstico de la lección 4 sigue siendo correcto —sobra— y aun así no es donde rinden tus dos días. La nota: "sobre-estructurado, 183 líneas para 17 de trabajo; se desmonta cuando haya que tocar asientos por cualquier motivo, y el plan de seis pasos está en el ticket #3311". Y una excepción digna de considerar: hay un riesgo que sí conviene atender ya y es barato —el descubrimiento dinámico importa todo lo que encuentra en la carpeta, y eso ya tiró el checkout cuarenta minutos—. Poner un try/except alrededor de la importación de cada módulo cuesta cuatro líneas y elimina la única forma en que ese rincón puede hacer daño. No es refactorizar: es contener el riesgo del código que decidiste no tocar, y muchas veces es la respuesta más inteligente.

Escenario 2 — desmóntalo, y es prioridad. ¿Bloquea? Sí, con nombre y con fecha: hay trabajo parado porque nadie quiere entrar. Esa sola respuesta justifica todo. Y el orden importa: primero se deja el rincón entendible sin cambiar comportamiento, y después se agrega el modo de grupos. Mezclarlos produce un cambio que nadie puede revisar.

Escenario 3 — no lo desmontes, y tampoco lo dejes como está. Este es el escenario que le da sentido al ejercicio. Con un contrato firmado, la variación deja de ser imaginaria: hay una segunda implementación real, con fecha, escrita por gente que no está en tu equipo. Las tres preguntas dan "sí" a las tres —se va a tocar, bloquea una entrega comprometida, y va a leerlo gente de fuera—. Pero la acción no es conservar el mecanismo tal cual: fue diseñado sin ningún consumidor real, y ahora hay uno que puede decir qué necesita. Hay que revisar la interfaz de cinco métodos con ese consumidor, resolver el supports() que devuelve True siempre —con dos plugins registrados, decide el orden del diccionario, que es una lotería—, y aislar la carga para que el fallo de un plugin de terceros no tumbe el checkout.

La conclusión que quiero que veas: el mismo código, con los mismos 183 líneas y la misma implementación única, recibe tres respuestas distintas —déjalo, quítalo, consérvalo y arréglalo— según lo que hay alrededor. El diagnóstico está en el código; la decisión está en el contexto. Por eso la primera sección del documento del proyecto final es el contexto: sin él, nadie puede evaluar el resto, ni siquiera tú.

Por qué funciona: si terminas esta guía con una sola idea, que sea esta. Las lecciones 3 y 4 te dieron diagnósticos, que son objetivos y se pueden verificar. Esta lección te dio la decisión, que es contextual y hay que argumentar. Confundir las dos es lo que produce tanto al que refactoriza todo como al que no refactoriza nada.

Resumen y siguiente paso

En esta lección aprendiste la habilidad más difícil del módulo: decidir no hacer nada, a propósito y por escrito. El código feo que funciona, que nadie toca y que nadie tiene que leer no es una prioridad, y tratarlo como si lo fuera te hace gastar el presupuesto de refactorización en los rincones que casualmente abriste, e introducir riesgo en un código que no lo tenía.

Separaste dos ejes que se confunden: feo y peligroso. Un defecto real es un error y se arregla con prioridad de error, no de limpieza. La casilla "feo y correcto" es la más grande de cualquier sistema con años, y su costo es diferido y condicional: no cuesta nada mientras nadie lo lea, y cuesta mucho el día que alguien tenga que cambiarlo.

Por eso las tres preguntas son todas sobre contacto humano futuro. ¿Se toca seguido? —se contesta con git log, y el conteo de archivos más tocados del proyecto casi siempre trae una sorpresa—. ¿Bloquea algo? —la más fuerte, porque convierte tu propuesta en una decisión de negocio; y recuerda que la deuda de diseño se paga cuando bloquea algo que importa, no cuando alguien decide pagarla—. ¿Alguien tiene que entenderlo pronto? —el contacto que no aparece en el historial: gente que entra, gente que se fue, código de dinero que se lee cada vez que alguien reclama—. Si las tres son "no": déjalo y anótalo.

Pusiste peso honesto en el otro plato de la balanza, con los cinco costos de refactorizar: tu tiempo, el tiempo de quien revisa, el riesgo de todo cambio, el costo de oportunidad, y el que nadie cuenta —el conocimiento que se pierde, porque cada refactorización puede borrar una cerca sin que nadie se entere, y esa probabilidad es mayor justo en el código viejo y sin documentar que más ganas dan de reescribir—.

Y te llevas la nota: qué se revisó, la evidencia con números, disparadores escritos como eventos observables y no como fechas, y el plan para quien tome el trabajo. Más el hábito de incluir en cada entrega la lista de lo que encontraste y no tocaste, que es la sección que más confianza genera y la que más rápido se escribe.

Antes de avanzar deberías poder: enunciar las tres preguntas y cómo se contesta cada una con evidencia; explicar por qué "no es prioridad" y "está bien" son cosas distintas; nombrar los cinco costos de refactorizar; y escribir una nota con disparadores.

Ya tienes las siete piezas. Leer antes de tocar, reconocer el patrón que quiere emerger, reconocer el que hay que quitar, ejecutar en pasos que nunca dejan el sistema roto, justificar con el tradeoff y no con el nombre, y decidir cuándo no hacer nada. La lección 8 es el proyecto final y las junta todas sobre los dos rincones de Boletia: en plugins/ vas a quitar la abstracción que no se gana su lugar; en checkout vas a introducir la que emerge del problema; y vas a entregar, además del código, el documento que defiende cada decisión con su tradeoff —incluida la de lo que dejaste en paz—. Se juzga por criterio y comunicación, no por cantidad de patrones. Y ahí cerramos la guía.

Recursos

  • TechnicalDebt y TechnicalDebtQuadrant (Martin Fowler) — la metáfora de la deuda con su distinción entre deuda prudente e imprudente, deliberada e inadvertida. La idea clave para esta lección: no toda deuda conviene pagarla, igual que no todo préstamo conviene liquidar antes de tiempo.
  • Yesterday's Weather / cómo el historial predice el trabajo futuro — sobre por qué la frecuencia de cambio pasada es el mejor predictor disponible de la frecuencia futura, que es el fundamento de la pregunta 1.
  • Your Code as a Crime Scene (Adam Tornhill) — el libro que convierte el historial del repositorio en la herramienta principal de priorización: puntos calientes, archivos que cambian juntos, complejidad cruzada con frecuencia de cambio. Es el desarrollo largo del comando de la pregunta 1.
  • The Boy Scout Rule (Robert C. Martin) — el consejo original, que conviene leer entero justamente para ver su condición: aplica a lo que ya estás tocando, no al archivo completo ni al sistema.