Módulo 2: Entender y fijar el legacy
Mini-proyecto: fija el precio del catálogo de Mercado
Descripción
Llegó el momento de juntar todo. En las siete lecciones anteriores fuiste aprendiendo las piezas por separado: qué es un characterization test y por qué fija comportamiento en vez de correctitud, cómo escribirlo capturando en vez de adivinar, cómo abrir un seam cuando una dependencia lo impide, cómo escalar con muestreo y golden master, qué hacer con las rarezas de las que alguien depende, y en qué orden aplicarlo todo. Este mini-proyecto te pone a usar las piezas juntas, de punta a punta, sobre el objetivo real del módulo: dejar el módulo catalog de Mercado completamente fijado —con una red de seguridad de verdad—, de modo que quede listo para lo que viene en el módulo 3, cuando empecemos a ponerlo detrás de un strangler facade y a desviarle tráfico.
El entregable no es un ensayo: es código ejecutado más una nota. Al terminar tendrás (1) un golden master de cientos de precios del catálogo, grabado con muestreo reproducible; (2) los seams que vuelven la caracterización determinista —la tasa de impuesto y el reloj parametrizados—; (3) la rareza gold caracterizada y documentada, no borrada, con una nota que explica quién depende de ella; y (4) la prueba de que puedes cambiar bajo la red —un refactor legible que pasa en verde, y un "arreglo" riesgoso que la red atrapa en rojo—. Esos cuatro artefactos son exactamente lo que el módulo 3 necesita para empezar a migrar con seguridad: sin ellos, desviar tráfico al servicio nuevo sería una apuesta a ciegas.
Conexión con el módulo. Esta lección no enseña nada nuevo: integra las siete anteriores en un flujo único y las ejecuta sobre un caso completo. Es el cierre del módulo 2 y la bisagra hacia el resto de la guía. La red que construyes aquí es la que en el módulo 3 certificará que el catálogo servido por el nuevo camino reproduce exactamente lo que servía el viejo (si el golden master sigue en verde contra el servicio nuevo, el desvío de tráfico es seguro); en el módulo 4, la que protegerá la migración de la implementación por dentro. La frontera se respeta hasta el final: aquí fijamos el catálogo; desviar su tráfico es M3, extraerlo a un servicio es M5, y la decisión formal de corregir la rareza (con su ADR) es la guía architecture-decisions. Este proyecto entrega la red; no cruza esas líneas.
Una analogía: el expediente que le entregas al siguiente equipo
Imagina que renovaste el baño —tomaste las fotos del "antes", marcaste dónde pasan los tubos y la llave de paso, anotaste que la pared torcida sostiene el techo— y ahora tienes que entregarle la obra a otro equipo que va a seguir con la cocina. No basta con decirles "ya está, sigan": les entregas un expediente. Las fotos del estado original (para que sepan contra qué comparar), el mapa de dónde están las instalaciones críticas (para que no rompan un tubo por error), y una nota grande y visible sobre la pared torcida ("NO tumbar: es de carga, el socio de arriba depende de ella"). Con ese expediente, el siguiente equipo puede trabajar con confianza: sabe qué había, qué no tocar, y cómo verificar que no rompió nada. Sin él, empieza a ciegas —justo el estado del que tú partiste—.
El entregable de este proyecto es ese expediente, para el catálogo de Mercado. El golden master son las fotos del estado original. Los seams son las llaves de paso documentadas (por aquí entra el reloj, por aquí la tasa de impuesto). La nota sobre la rareza gold es el cartel de "NO tumbar sin avisar al socio". Y la demostración de que un refactor pasa en verde y un "arreglo" en rojo es la prueba de que el expediente funciona —de que la red efectivamente distingue lo seguro de lo peligroso—. Cuando el módulo 3 tome este catálogo para migrarlo, va a trabajar con tu expediente en la mano. La calidad de ese expediente es lo que decide si la migración avanza con confianza o a tientas.
El proyecto, ejecutado: el expediente completo del catálogo
Aquí está el proyecto de punta a punta. Lee el código como el flujo que integra las siete lecciones: seams (tasa e impuesto y reloj parametrizados), golden master por muestreo de 400 entradas, la rareza documentada, y dos cambios bajo la red —un refactor legible y un "arreglo" que rompe la rareza—.
import math
import random
from datetime import date
CATEGORY_DISCOUNT = {"electronics": 0.05, "books": 0.10, "toys": 0.08, "grocery": 0.00}
BULK_MIN_QTY, BULK_DISCOUNT, GOLD_LOYALTY_DISCOUNT = 10, 0.05, 0.03
def _floor_cents(a):
return math.floor(a * 100) / 100
# --- Legacy con SEAMS: tax_rate y today entran como parametros con default ---
def legacy_catalog_price(item, tax_rate=0.16, today=None):
if today is None:
today = date.today()
price = _floor_cents(item["unit_price"] * item["quantity"])
price = _floor_cents(price * (1 - CATEGORY_DISCOUNT.get(item["category"], 0.0)))
if item["quantity"] >= BULK_MIN_QTY:
price = _floor_cents(price * (1 - BULK_DISCOUNT))
coupon = item.get("coupon_percent", 0.0)
if coupon and (not item.get("coupon_expires") or today <= item["coupon_expires"]):
price = _floor_cents(price * (1 - coupon))
price = _floor_cents(price * (1 + tax_rate))
if item.get("loyalty") == "gold": # RAREZA: gold post-impuesto
price = _floor_cents(price * (1 - GOLD_LOYALTY_DISCOUNT))
return price
def generate_catalog_inputs(n, seed=2026):
rng = random.Random(seed)
cats = list(CATEGORY_DISCOUNT)
items = []
for i in range(n):
items.append({
"sku": f"CAT-{i:04d}",
"unit_price": round(rng.uniform(1, 400), 2),
"quantity": rng.randint(1, 20),
"category": rng.choice(cats),
"coupon_percent": rng.choice([0.0, 0.0, 0.10, 0.15]),
"coupon_expires": date(2026, 12, 31),
"loyalty": rng.choice([None, None, None, "gold"]),
})
return items
FIXED_TODAY = date(2026, 7, 1) # reloj fijo -> deterministico
FIXED_TAX = 0.16 # tasa fija -> deterministico
inputs = generate_catalog_inputs(400)
# 1) GOLDEN MASTER: la foto del "como cobra hoy" el catalogo.
golden_master = {
it["sku"]: legacy_catalog_price(it, tax_rate=FIXED_TAX, today=FIXED_TODAY)
for it in inputs
}
print("== 1. Red de seguridad: golden_master del catalogo ==")
print(f" {len(golden_master)} precios fijados con tax={FIXED_TAX}, today={FIXED_TODAY}")
# 2) Verifica que la red este VERDE contra el propio legacy (control).
selfcheck = sum(
1 for it in inputs
if legacy_catalog_price(it, tax_rate=FIXED_TAX, today=FIXED_TODAY)
!= golden_master[it["sku"]]
)
print(f" auto-verificacion legacy vs golden_master: "
f"{'VERDE' if selfcheck == 0 else 'ROJO'} ({selfcheck} fallos)\n")
# 3) La rareza documentada, no borrada.
print("== 2. Rareza caracterizada (no 'arreglada') ==")
gold_orders = [it for it in inputs if it.get("loyalty") == "gold"]
print(f" {len(gold_orders)} ordenes gold en el lote aplican la lealtad DESPUES")
print(" del impuesto. Queda fijada en el golden_master. Nota para el equipo:")
print(" 'el rebate del socio depende de este numero; cambiarlo es una decision")
print(" de negocio, no una limpieza tecnica.'\n")
# 4) Refactor SEGURO detras de la red.
def refactored_catalog_price(item, tax_rate=0.16, today=None):
if today is None:
today = date.today()
def step_base(it):
return _floor_cents(it["unit_price"] * it["quantity"])
def step_category(p, it):
return _floor_cents(p * (1 - CATEGORY_DISCOUNT.get(it["category"], 0.0)))
def step_bulk(p, it):
return _floor_cents(p * (1 - BULK_DISCOUNT)) if it["quantity"] >= BULK_MIN_QTY else p
def step_coupon(p, it):
c = it.get("coupon_percent", 0.0)
if c and (not it.get("coupon_expires") or today <= it["coupon_expires"]):
return _floor_cents(p * (1 - c))
return p
price = step_base(item)
price = step_category(price, item)
price = step_bulk(price, item)
price = step_coupon(price, item)
price = _floor_cents(price * (1 + tax_rate))
if item.get("loyalty") == "gold":
price = _floor_cents(price * (1 - GOLD_LOYALTY_DISCOUNT))
return price
# 5) Un "arreglo" que rompe la rareza (gold pre-impuesto).
def cleaned_catalog_price(item, tax_rate=0.16, today=None):
if today is None:
today = date.today()
price = _floor_cents(item["unit_price"] * item["quantity"])
price = _floor_cents(price * (1 - CATEGORY_DISCOUNT.get(item["category"], 0.0)))
if item["quantity"] >= BULK_MIN_QTY:
price = _floor_cents(price * (1 - BULK_DISCOUNT))
coupon = item.get("coupon_percent", 0.0)
if coupon and (not item.get("coupon_expires") or today <= item["coupon_expires"]):
price = _floor_cents(price * (1 - coupon))
if item.get("loyalty") == "gold":
price = _floor_cents(price * (1 - GOLD_LOYALTY_DISCOUNT))
return _floor_cents(price * (1 + tax_rate))
def run_suite(fn, label):
fails = [it["sku"] for it in inputs
if fn(it, tax_rate=FIXED_TAX, today=FIXED_TODAY) != golden_master[it["sku"]]]
print(f" characterization_suite vs {label}:")
print(f" {len(inputs)-len(fails)} passed, {len(fails)} failed ==> "
f"{'VERDE' if not fails else 'ROJO'}")
if fails:
print(f" ejemplos: {', '.join(fails[:4])} ...")
return len(fails)
print("== 3. Cambiar bajo la red ==")
run_suite(refactored_catalog_price, "el REFACTOR legible (mismo comportamiento)")
run_suite(cleaned_catalog_price, "el 'arreglo' de la rareza gold (cambia comportamiento)")
print()
print("== Entregable ==")
print(" [x] golden_master de 400 precios (la foto del catalogo de hoy)")
print(" [x] seams: tax_rate y today parametrizados (test deterministico)")
print(" [x] rareza gold documentada y fijada, no borrada")
print(" [x] refactor legible entregado en VERDE")
print(" [x] 'arreglo' riesgoso atrapado en ROJO antes de produccion")
print(" Listo para M3: poner el catalogo detras de un strangler facade.")
Qué esperar. Al correr el archivo, la salida es exactamente esta:
== 1. Red de seguridad: golden_master del catalogo ==
400 precios fijados con tax=0.16, today=2026-07-01
auto-verificacion legacy vs golden_master: VERDE (0 fallos)
== 2. Rareza caracterizada (no 'arreglada') ==
86 ordenes gold en el lote aplican la lealtad DESPUES
del impuesto. Queda fijada en el golden_master. Nota para el equipo:
'el rebate del socio depende de este numero; cambiarlo es una decision
de negocio, no una limpieza tecnica.'
== 3. Cambiar bajo la red ==
characterization_suite vs el REFACTOR legible (mismo comportamiento):
400 passed, 0 failed ==> VERDE
characterization_suite vs el 'arreglo' de la rareza gold (cambia comportamiento):
362 passed, 38 failed ==> ROJO
ejemplos: CAT-0000, CAT-0010, CAT-0011, CAT-0016 ...
== Entregable ==
[x] golden_master de 400 precios (la foto del catalogo de hoy)
[x] seams: tax_rate y today parametrizados (test deterministico)
[x] rareza gold documentada y fijada, no borrada
[x] refactor legible entregado en VERDE
[x] 'arreglo' riesgoso atrapado en ROJO antes de produccion
Listo para M3: poner el catalogo detras de un strangler facade.
Recorre el expediente sección por sección, porque cada una es una lección del módulo hecha artefacto.
La sección 1 —el golden master— es la red de seguridad completa. Se generaron 400 entradas del catálogo por muestreo reproducible (semilla 2026) y se grabaron sus 400 precios como la foto del "cómo cobra hoy". Fíjate en el detalle crucial: se grabaron con tax=0.16 y today=2026-07-01 fijos. Eso es lo que hace la foto nítida en vez de movida —sin fijar esas dos dependencias, cada corrida daría números distintos y el golden master no significaría nada—. La auto-verificación sale verde (0 fallos): el legacy contra su propio golden master coincide, como debe ser. La red está tendida.
La sección 2 —la rareza documentada— es la nota del expediente. De las 400 órdenes, 86 son de clientes gold, y todas aplican la lealtad después del impuesto —la rareza—. El proyecto no la borra: la fija en el golden master y le pone una nota explícita para el siguiente equipo: "el rebate del socio depende de este número; cambiarlo es una decisión de negocio, no una limpieza técnica". Ese cartel es lo que evita que el próximo desarrollador tumbe la pared de carga sin saberlo. La rareza pasó de estar escondida en el código a estar documentada y protegida.
La sección 3 —cambiar bajo la red— es la prueba de que el expediente funciona. Se le hicieron dos cambios al catálogo, bajo la misma red de 400 precios. El refactor legible —que extrajo cada paso a una función con nombre (step_base, step_category, etc.), preservando el orden y el redondeo, y manteniendo la rareza gold después del impuesto— pasa en verde: 400 de 400. La red certifica que, a pesar de reorganizar toda la función, el comportamiento es idéntico. El "arreglo" de la rareza —que movió gold a antes del impuesto— pasa en rojo: 38 fallos (los casos gold donde el redondeo hace que el centavo caiga distinto). La red atrapó, antes de producción, exactamente el cambio que habría roto la conciliación con el socio de la lección 6. Un cambio seguro y un cambio peligroso, distinguidos objetivamente por el árbitro.
Y el entregable cierra el módulo: cinco casillas marcadas, y la línea que importa —"listo para M3"—. El catálogo ya no es código que da miedo tocar; es código con red, con seams documentados, con su rareza señalada, y con la prueba de que la red distingue lo seguro de lo peligroso. Eso es exactamente lo que el strangler facade del módulo 3 necesita para empezar a desviarle tráfico con confianza.
Profundización: por qué este expediente es el prerrequisito de toda la migración
Vale la pena hacer explícito por qué el módulo 3 —y todo lo que sigue— no puede empezar sin este expediente. El strangler fig, que verás en M3, funciona poniendo un facade delante del catálogo que enruta parte del tráfico al servicio nuevo y el resto al viejo, y va subiendo el porcentaje del nuevo del 0 al 100 hasta apagar el viejo. La pregunta que decide si eso es seguro es una sola: ¿el servicio nuevo hace exactamente lo que hacía el viejo?. Y esa pregunta solo tiene respuesta si tienes el golden master. Con él, corres el servicio nuevo contra las 400 entradas fijadas y comparas: si sale verde, el nuevo reproduce el comportamiento del viejo —incluida la rareza gold, incluida cada decisión de redondeo— y puedes desviarle tráfico con confianza. Si sale rojo, el nuevo difiere en algo, y desviarle tráfico cobraría precios distintos a clientes reales. El golden master convierte "creo que el servicio nuevo está bien" en "lo verifiqué contra 400 casos".
Por eso el orden de la guía es el que es. No se puede desviar tráfico (M3) sin la red (M2). No se puede migrar la implementación por dentro (M4) sin la red. No se puede extraer el servicio (M5) sin la red que garantice que el extraído se comporta como el original. La red de este proyecto es el cimiento, y los seams son parte esencial de ese cimiento: sin parametrizar el reloj y la tasa de impuesto, ni siquiera podrías grabar un golden master reproducible —cada corrida daría otra foto—. El expediente no es un paso opcional de calidad; es la condición técnica que hace verificable todo lo que viene.
Hay una última pieza del expediente que conviene nombrar, porque es la que ata este módulo con la lección 6 y con la guía de decisiones: la nota sobre la rareza es un traspaso de conocimiento tácito. El módulo 1 advirtió que las reescrituras fracasan en parte porque pierden el conocimiento tácito enterrado en el legacy —reglas ganadas en años que nadie documentó—. Este proyecto hace lo contrario: toma una de esas reglas tácitas (gold después del impuesto, el rebate del socio) y la explicita —la fija en un test, le pone una nota, señala al dependiente—. Cada rareza que caracterizas y documentas es un pedazo de conocimiento tácito rescatado del olvido. La migración incremental gana, en parte, porque va convirtiendo ese conocimiento invisible en artefactos visibles, rebanada por rebanada, en vez de tirarlo a la basura de un big-bang.
flowchart TD
P["Expediente del catalogo (M2)"] --> GM["golden_master<br/>(400 precios fijados)"]
P --> SE["seams<br/>(tax_rate + today)"]
P --> NOTE["nota de la rareza<br/>(gold: el socio depende)"]
GM --> M3["M3: strangler facade<br/>desvia trafico si el nuevo == golden_master"]
SE --> M3
NOTE --> M3
M3 --> M5["M5: extraer el servicio<br/>con la red garantizando paridad"]
Errores comunes
Entregar el golden master sin los seams, con una foto no reproducible. Qué pasa: se graba el golden master pero el legacy sigue consultando el reloj real y la global de impuesto por dentro, así que la foto cambia según el día y la configuración. Por qué pasa: grabar los números se siente como "ya está la red", y los seams parecen un refinamiento posterior. Cómo detectarlo: si corres la grabación en dos días distintos y el golden master sale diferente, la foto está movida y no sirve para comparar. Cómo corregirlo: los seams no son opcionales para un golden master reproducible —son lo que fija las dependencias no deterministas para que la foto sea siempre la misma—. Este proyecto grabó con tax=0.16 y today=2026-07-01 fijos justamente por eso. Sin seams, el módulo 3 no podría comparar el servicio nuevo contra una foto estable, porque la foto se movería sola.
Borrar o "limpiar" la rareza al entregar, en vez de documentarla. Qué pasa: al armar el expediente, el equipo aprovecha para "dejar el catálogo bonito" y corrige la rareza gold antes de entregar. Por qué pasa: entregar código "limpio" se siente como entregar mejor trabajo. Cómo detectarlo: si el golden master que entregas ya no contiene la rareza (los precios gold salen 106.89 en vez de 106.88), cambiaste el comportamiento que debías preservar. Cómo corregirlo: el expediente debe fijar el comportamiento actual, rareza incluida, y documentarla —no borrarla—. La rareza es un contrato de facto (el socio depende, lección 6); corregirla es una decisión de negocio coordinada, no una limpieza de entrega. Un golden master "limpiado" es peor que inútil: le entrega al módulo 3 una foto de un catálogo que no existe, y cuando el servicio nuevo reproduzca el comportamiento real (con rareza), la red saldrá roja contra la foto equivocada.
Dar el proyecto por terminado sin probar que la red atrapa algo. Qué pasa: se graba el golden master, sale verde contra el propio legacy, y se declara la red lista —sin probar nunca que detecta un cambio real—. Por qué pasa: el verde de la auto-verificación da una sensación de completitud. Cómo detectarlo: si nunca corriste la red contra una implementación distinta del legacy, no sabes si de verdad atrapa regresiones o si siempre sale verde por casualidad (por ejemplo, si el golden master tuviera un bug que lo hace coincidir con todo). Cómo corregirlo: una red no está validada hasta que la ves ponerse roja ante un cambio de comportamiento —por eso este proyecto corre el "arreglo" de la rareza y confirma los 38 fallos—. El rojo del cambio peligroso es tan parte del entregable como el verde del refactor seguro: juntos prueban que la red distingue, que no es un semáforo pegado en verde. Entregar una red que nunca has visto en rojo es entregar un detector de humo sin haber apretado el botón de prueba.
Ejercicios
Ejercicio 1 — Extiende el expediente. El golden master del proyecto se grabó con muestreo aleatorio de 400 entradas. Un revisor señala que, aunque salieron 86 casos gold, el muestreo no garantiza cubrir el borde del umbral de volumen (cantidad exactamente 10). Describe cómo extenderías generate_catalog_inputs para sembrar a propósito los casos borde críticos del catálogo, y explica por qué eso hace el expediente más robusto para el módulo 3.
Ver solución
Extendería la generación combinando el muestreo aleatorio reproducible con un bloque de casos borde forzados que se agregan siempre, independientemente del azar. En concreto, sembraría explícitamente:
- El umbral de volumen: para varios precios y categorías, ítems con
quantityfijada en 9, 10 y 11 —los tres puntos alrededor deBULK_MIN_QTY—, para fijar tanto el caso sin descuento por volumen (9) como el borde exacto (10) y el siguiente (11). - Todas las categorías, incluida grocery (descuento 0): al menos varios ítems de cada categoría, para que la rama de cada una quede fijada, incluida la que "no descuenta".
- Cupón vigente vs vencido: ítems con
coupon_expiresantes y después delFIXED_TODAY, para fijar ambas ramas del cupón (el seam del reloj hace esto determinista). - Clientes gold en proporción garantizada: un bloque explícito de casos gold (por ejemplo, combinados con cada categoría y con cupón), para no depender de que el azar produzca suficientes —y suficientes de los que manifiestan la rareza, no solo de los que la absorben en el redondeo—.
Estos casos forzados se concatenan con las 400 (o las que sean) entradas aleatorias con semilla fija, que siguen barriendo el espacio general.
Por qué hace el expediente más robusto para M3: el golden master es la vara contra la que el módulo 3 medirá si el servicio nuevo reproduce el viejo. Si esa vara tiene hoyos en los bordes —no fija el umbral de volumen, no cubre grocery, no incluye cupón vencido—, entonces un servicio nuevo que se equivoque justo en esos bordes pasaría la comparación en verde, y el error se desviaría a producción cuando el tráfico llegara a un cliente con cantidad 10 o cupón vencido. Sembrar los bordes cierra esos hoyos: garantiza que la foto cubra los puntos finos donde el legacy es más frágil, así que cualquier divergencia del servicio nuevo en esos puntos se detecta antes de desviarle tráfico. Una vara con hoyos da falsa confianza a toda la migración que se apoya en ella.
Ejercicio 2 — Simula el handoff a M3. El módulo 3 va a construir un modern_catalog_price (un servicio nuevo) que debe reproducir el comportamiento del legacy. Describe, usando el golden master de este proyecto, el procedimiento exacto con el que el módulo 3 verificaría que es seguro empezar a desviarle tráfico. ¿Qué resultado del procedimiento daría luz verde, y qué resultado obligaría a detener el desvío?
Ver solución
El procedimiento es un parallel-run del servicio nuevo contra el golden master de este proyecto:
- Correr el servicio nuevo sobre las mismas 400 entradas fijadas, con los mismos valores de los seams (
tax_rate=0.16,today=2026-07-01) con que se grabó el golden master —para comparar contra la misma escena—. - Comparar, entrada por entrada, la salida de
modern_catalog_pricecontragolden_master[sku], contando y listando las discrepancias. - Leer el resultado:
- Luz verde (400 passed, 0 failed): el servicio nuevo reproduce exactamente el comportamiento del legacy para las 400 entradas —incluida la rareza gold (precios gold en 106.88, no 106.89), incluido el redondeo por pasos, incluidas las ramas del umbral y del cupón—. Es seguro empezar a desviarle tráfico, porque cualquier cliente que caiga en el servicio nuevo recibirá el mismo precio que le daba el viejo.
- Detener el desvío (cualquier failed > 0): el servicio nuevo difiere del legacy en al menos un caso. El diff dice exactamente cuáles y cómo. Desviar tráfico ahora cobraría precios distintos a clientes reales en esos casos. Hay que arreglar el servicio nuevo para que reproduzca el comportamiento (bug-for-bug, incluida la rareza) y volver a correr el parallel-run hasta que salga verde.
El punto clave del handoff: el módulo 3 no decide "el servicio nuevo se ve bien, empecemos"; decide con la evidencia del golden master. La red de este proyecto es lo que convierte el desvío de tráfico de una apuesta ("espero que el nuevo esté bien") en una operación verificada ("comparé el nuevo contra 400 casos del viejo y coinciden"). Por eso el expediente es el prerrequisito de M3: sin él, no habría forma objetiva de dar la luz verde.
Ejercicio 3 — El expediente incompleto. Un compañero entrega su versión del proyecto así: grabó el golden master, pero (a) lo grabó sin fijar today (usó el reloj real), (b) "de paso" corrigió la rareza gold para dejar el código limpio, y (c) nunca corrió la red contra ninguna implementación distinta del legacy. Para cada uno de los tres problemas, explica qué se rompe y cómo lo detectarías, y di cuál de los tres es el más peligroso para el módulo 3 y por qué.
Ver solución
- (a) Golden master sin fijar
today: la foto no es reproducible —depende del día en que se grabó, porque la vigencia de los cupones cambia con el reloj—. Se rompe la comparabilidad: si el módulo 3 corre el parallel-run otro día, el golden master ya no coincide ni con el propio legacy, y la red da rojos falsos (o verdes falsos) por el calendario, no por el comportamiento. Cómo detectarlo: regrabar en dos fechas distintas y ver que el golden master cambia sin que nadie tocara el código. La corrección es usar el seam del reloj con una fecha fija, como hizo el proyecto (today=2026-07-01). - (b) "Corrigió" la rareza gold: el golden master ahora fija un comportamiento que el legacy no tiene (precios gold en 106.89 en vez de 106.88). Se rompe la fidelidad de la foto: ya no retrata el catálogo real. Cómo detectarlo: comparar el golden master del compañero contra el legacy real —saldría rojo en los casos gold—, o notar que ningún precio gold termina en la rareza esperada. Además, borró un contrato de facto (el rebate del socio) sin coordinación.
- (c) Nunca vio la red en rojo: no hay prueba de que la red detecte cambios; podría estar siempre en verde por un error. Se rompe la validación de la red misma. Cómo detectarlo: correrla contra una implementación deliberadamente distinta (como el "arreglo" del proyecto) y confirmar que se pone roja; si no lo hace, la red no sirve.
El más peligroso para M3 es (b), la rareza "corregida". Las razones: (a) produce fallos ruidosos —rojos o verdes que no cuadran por el calendario— que alguien notará y hará investigar; es un problema visible. (c) deja la red sin validar, pero mientras el legacy y el nuevo coincidan de verdad, no causa un daño activo inmediato. En cambio (b) es silencioso y activamente engañoso: el golden master se ve perfectamente sano y sale verde contra sí mismo, pero retrata un catálogo que no existe. Cuando el módulo 3 construya el servicio nuevo reproduciendo el comportamiento real del legacy (con la rareza, que es lo correcto durante la migración), el parallel-run saldrá rojo contra la foto falsa, y el equipo podría "arreglar" el servicio nuevo para que coincida con la foto equivocada —propagando la corrección no coordinada de la rareza al sistema nuevo y rompiendo al socio, todo mientras cree estar haciendo lo correcto—. Una foto infiel corrompe todas las decisiones que se apoyan en ella; es el error que más lejos llega.
Resumen y siguiente paso
Con este mini-proyecto cerraste el módulo 2 entregando el expediente completo del catálogo de Mercado: un golden master de 400 precios grabado por muestreo reproducible, los seams (tasa de impuesto y reloj) que lo vuelven determinista, la rareza gold caracterizada y documentada —no borrada—, y la prueba de que la red distingue lo seguro de lo peligroso (un refactor legible en verde, 400/400; un "arreglo" de la rareza en rojo, 38 fallos). Viste, con el expediente que le entregas al siguiente equipo, que la red no es un adorno de calidad: es el traspaso de conocimiento que hace verificable toda la migración que viene. El catálogo pasó de ser código que da miedo tocar a ser código con red, con seams, con su rareza señalada y con la garantía de que cualquier cambio se puede juzgar objetivamente.
Con esto completaste el módulo entero. Ahora puedes: fijar el comportamiento de una función legacy con un characterization test; abrir seams para volverla determinista; escalar la caracterización con muestreo y golden master; reconocer y documentar las rarezas de las que alguien depende; y aplicar la disciplina de cinco pasos —leer, seam, caracterizar, cambiar, dejar que la red decida— para cambiar sin miedo.
El módulo 3 toma este catálogo fijado y da el primer paso de la migración de verdad: el patrón strangler fig. Vas a poner un facade delante del catálogo, construir el servicio nuevo al lado, y desviar tráfico incrementalmente —del 0% al 100%— con la vieja ruta como fallback, hasta apagar el legacy. Y en cada paso de ese desvío, la red que construiste en este proyecto será la que garantice que el servicio nuevo hace exactamente lo que hacía el viejo —rareza gold incluida—. Por primera vez en la guía, vas a mover tráfico de lo viejo a lo nuevo, y lo vas a hacer con seguridad porque el catálogo ya está fijado. El expediente está listo; empieza la obra.
Recursos
- Michael Feathers, Working Effectively with Legacy Code (Prentice Hall, 2004) — el libro completo como referencia del proyecto: characterization tests (cap. 13), seams (cap. 4), y el flujo de poner código bajo test antes de cambiarlo. La caja de herramientas de este módulo. En inglés.
- Martin Fowler, "StranglerFigApplication" — martinfowler.com/bliki/StranglerFigApplication.html. Hacia dónde va este expediente: el patrón que el módulo 3 usará para desviar tráfico del catálogo viejo al nuevo, apoyado en la red que aquí construiste. En inglés.
- Sam Newman, Monolith to Microservices (O'Reilly, 2019), cap. 3 "Splitting the Monolith" — cómo se prepara y se extrae una pieza del monolito (como el catálogo), y por qué la red de caracterización es el prerrequisito de la extracción. El puente de M2 a M3 y M5. En inglés.
- Martin Fowler, "Patterns of Legacy Displacement" — martinfowler.com/articles/patterns-legacy-displacement. El procedimiento con que el módulo 3 verificará el servicio nuevo contra el golden master de este proyecto: correr ambos y comparar; el parallel run como patrón nombrado es de Sam Newman (Monolith to Microservices). En inglés.