Módulo 8: Proyecto — modernizar una rebanada de Mercado
Elegir y caracterizar la rebanada
Descripción
El método empieza aquí, y empieza como debe: antes de tocar nada. La lección 1 te dio el mapa del ascenso; esta da el primer paso, que integra dos módulos —el 1 (por qué modernizar por rebanadas) y el 2 (caracterizar el legacy)— en una sola decisión y una sola red de seguridad. Primero eliges qué rebanada modernizar y por qué el catalog es la que conviene primero. Luego, y esto es lo que ningún equipo prudente se salta, congelas su comportamiento actual en un golden master —incluidas sus rarezas— para que cualquier cosa que hagas después se compare contra una foto del "antes" y no puedas romper algo sin enterarte.
Modernizar por rebanadas, no de golpe, fue la lección del módulo 1: la reescritura grande fracasa, y el camino que funciona es incremental. Pero "incremental" plantea de inmediato una pregunta: ¿cuál rebanada primero? La respuesta no es al azar. Se elige la rebanada que tenga menos dependencias hacia el resto del monolito —la "hoja" del grafo de dependencias, el módulo que menos necesita de los demás—, porque es la que se puede extraer con menos hilos que cortar. En Mercado, ese es el catalog: orders depende de catalog (necesita saber qué productos existen y cuánto cuestan), payments depende de orders, shipping depende de orders —pero catalog no depende de ninguno—. Es la hoja. Empezar por ahí es empezar por lo que menos duele.
Elegida la rebanada, viene la red. El catalog calcula precios, y ese cálculo tiene rarezas —comportamientos extraños que llevan años en producción y de los que, sin que lo sepas, alguien depende—. Antes de reimplementar una sola línea, grabas un golden master: corres el catalog legacy sobre un conjunto de casos, guardas sus salidas exactas (rarezas y todo), y ese registro se vuelve la referencia canónica del "cómo funciona hoy". Cualquier reimplementación candidata se compara contra él en un parallel-run, y el diff te dice, sin que tengas que adivinar, dónde cambiaste el comportamiento. Como vas a ver, una reimplementación "limpia" —bienintencionada, que "arregla" lo que parecía un bug— cambia números que estaban bien, y el golden master la atrapa.
Conexión con el módulo. Esta es la primera técnica del método (M1 + M2), y produce el artefacto del que dependen todas las lecciones siguientes: el golden master. La lección 3 lo usará como referencia cuando el strangler router desvíe el tráfico (el modern se compara contra él); la lección 6 lo usará para cerrar el último tramo del burn-down (el modern implementa el descuento por volumen guiado por el golden master); y la lección 7 lo pondrá como una de las condiciones de done (la characterization en verde). Fíjate en la frontera: aquí usamos el characterization test como herramienta de migración —congelar el comportamiento para poder cambiar con seguridad—, no como teoría de testing; la mecánica del golden master, el muestreo y el approval testing a fondo son del ecosistema de Testing. Aquí es la red de seguridad del primer paso.
Una analogía: fotografiar la casa antes de la remodelación
Cuando una aseguradora va a cubrir una remodelación, hace algo antes de que el primer albañil toque un muro: fotografía la casa completa, cuarto por cuarto. No para admirarla —para tener un registro objetivo del "estado antes"—. Si al final de la obra aparece una grieta en la sala, la pregunta "¿esta grieta ya estaba?" no se resuelve con la memoria de nadie (que es interesada y borrosa): se resuelve comparando contra las fotos. Si la grieta está en la foto, ya existía; si no está, la causó la obra. Las fotos convierten una discusión de opiniones en una comparación de hechos.
Y fíjate en un detalle crucial de esas fotos: registran la casa tal como está, imperfecciones incluidas. Si la sala ya tenía una mancha de humedad en el techo, la foto la captura. La aseguradora no "arregla" la mancha en la foto ni la omite "porque es fea"; la registra tal cual, porque el punto no es que la casa sea perfecta, sino tener un registro fiel del estado real. Si un albañil, con buena intención, tapara esa mancha creyendo que es un defecto, y resulta que la mancha marcaba una fuga que el dueño estaba vigilando, "arreglarla" sin avisar sería un problema —cambió algo de lo que alguien dependía—.
El golden master es esa fotografía de la casa. Grabas la salida del catalog legacy para un conjunto de casos —esa es tu foto del "antes", rarezas y todo—. Y cuando reimplementes el cálculo, comparas contra la foto: si un número cambió, lo sabes, y sabes exactamente cuál. Las rarezas del legacy son las manchas de humedad: parecen defectos, pero el golden master las registra fielmente, porque a lo mejor alguien —el checkout, un socio, una conciliación— depende de ese número exacto. Fotografías antes de tocar, para que ninguna grieta nueva pase por vieja.
Ejemplo trabajado: el golden master del catalog y la regresión atrapada
Vamos a ejecutar el primer paso completo. El catalog de Mercado calcula precios con dos rarezas reales: (1) el descuento por volumen (a partir de 10 unidades) trunca el subtotal a la decena de centavo más baja —una herencia de un sistema viejo que solo manejaba precios en decenas—, y (2) el producto inactivo se cobra igual —el legacy nunca mira el campo active al calcular el precio, y el checkout depende de eso para los items que ya están en el carrito—. Grabamos el golden master de 6 casos y corremos contra él una reimplementación "limpia" que, con toda la buena intención, redondea el bulk de forma normal y no cobra los inactivos. El golden master atrapa lo que la limpieza rompió.
# Paso 1 del metodo: caracterizar el catalog ANTES de tocarlo. El golden master
# congela el comportamiento actual del legacy -rarezas incluidas- y atrapa la
# regresion cuando una reimplementacion "limpia" cambia el numero.
import math
TAX_RATE = 0.16
BULK_MIN_QTY = 10
BULK_DISCOUNT = 0.05
def legacy_price(unit_price_cents, quantity, active):
"""El calculo del catalog legacy, con sus DOS rarezas."""
subtotal = unit_price_cents * quantity
# RAREZA 1: el descuento por volumen TRUNCA a la decena de centavo mas baja
# (una herencia de un sistema viejo que solo manejaba precios en decenas).
if quantity >= BULK_MIN_QTY:
discounted = subtotal * (1 - BULK_DISCOUNT)
subtotal = math.floor(discounted / 10) * 10
total = math.floor(subtotal * (1 + TAX_RATE))
# RAREZA 2: el producto inactivo se cobra IGUAL (el legacy nunca mira 'active'
# para el precio; el checkout depende de esto para items ya en el carrito).
return total
def clean_price(unit_price_cents, quantity, active):
"""Reimplementacion 'limpia': redondea el bulk normal y no cobra inactivos."""
if not active:
return 0 # "limpieza": no cobrar inactivos
subtotal = unit_price_cents * quantity
if quantity >= BULK_MIN_QTY:
subtotal = round(subtotal * (1 - BULK_DISCOUNT)) # "limpieza": redondeo normal
return math.floor(subtotal * (1 + TAX_RATE))
# --- El golden master: 6 casos que barren las rarezas del catalog. ---
cases = [
# (sku, unit_price_cents, quantity, active)
("ssd-1tb", 8999, 1, True), # normal
("usb-hub", 3499, 10, True), # bulk: dispara la rareza del truncado
("kbd-mech", 7333, 12, True), # bulk con numeros "sucios"
("mouse-pro", 2499, 3, True), # normal
("cable-hdmi", 1299, 25, True), # bulk grande
("webcam-hd", 5999, 2, False), # INACTIVO: el legacy lo cobra igual
]
golden_master = [(sku, legacy_price(p, q, a)) for sku, p, q, a in cases]
print("Golden master del catalog legacy (6 casos, precio en centavos)\n")
for sku, price in golden_master:
print(f" {sku:<11} legacy_price = {price}")
print()
# --- Parallel-run: la reimplementacion "limpia" contra el golden master. ---
print("parallel_run: clean_price vs golden_master")
print(f" {'sku':<11}{'golden':>8}{'clean':>8}{'diff?':>7}")
print(" " + "-" * 34)
diffs = []
for (sku, expected), (_, p, q, a) in zip(golden_master, cases):
actual = clean_price(p, q, a)
mark = "OK" if actual == expected else "DIFF"
if actual != expected:
diffs.append((sku, expected, actual))
print(f" {sku:<11}{expected:>8}{actual:>8}{mark:>7}")
print(" " + "-" * 34)
print(f"\n {len(cases) - len(diffs)} coinciden, {len(diffs)} difieren.")
for sku, expected, actual in diffs:
print(f" REGRESION en {sku}: legacy={expected}, clean={actual}")
print("\n El golden master atrapo la regresion: la reimplementacion 'limpia' cambio")
print(" el numero en el bulk (redondeo) y en el inactivo (dejo de cobrarlo). Antes")
print(" de tocar el catalog, su comportamiento -rarezas incluidas- quedo congelado.")
Qué esperar. Al correr el archivo, la salida es exactamente esta:
Golden master del catalog legacy (6 casos, precio en centavos)
ssd-1tb legacy_price = 10438
usb-hub legacy_price = 38558
kbd-mech legacy_price = 96964
mouse-pro legacy_price = 8696
cable-hdmi legacy_price = 35786
webcam-hd legacy_price = 13917
parallel_run: clean_price vs golden_master
sku golden clean diff?
----------------------------------
ssd-1tb 10438 10438 OK
usb-hub 38558 38558 OK
kbd-mech 96964 96971 DIFF
mouse-pro 8696 8696 OK
cable-hdmi 35786 35787 DIFF
webcam-hd 13917 0 DIFF
----------------------------------
3 coinciden, 3 difieren.
REGRESION en kbd-mech: legacy=96964, clean=96971
REGRESION en cable-hdmi: legacy=35786, clean=35787
REGRESION en webcam-hd: legacy=13917, clean=0
El golden master atrapo la regresion: la reimplementacion 'limpia' cambio
el numero en el bulk (redondeo) y en el inactivo (dejo de cobrarlo). Antes
de tocar el catalog, su comportamiento -rarezas incluidas- quedo congelado.
Lee el resultado en dos partes, porque juntas cuentan el primer paso del método.
La foto: el golden master. Los seis números de arriba son la fotografía del catalog legacy —el "cómo funciona hoy", en centavos, rarezas incluidas—. El ssd-1tb cuesta 10438 (un producto normal con impuesto), el usb-hub en cantidad de 10 cuesta 38558 (con el descuento por volumen y su truncado raro), y el webcam-hd, aunque está inactivo, cuesta 13917 —el legacy lo cobra igual—. No juzgamos si estos números son "correctos"; solo los registramos como la referencia. Son las fotos de la casa antes de la obra.
La comparación: el parallel-run. La reimplementación "limpia" corre sobre los mismos casos y se compara contra la foto. De los 6 casos, 3 coinciden y 3 difieren —y las 3 diferencias son exactamente las dos rarezas que la limpieza "arregló"—:
webcam-hd: 13917 → 0. La reimplementación limpia decidió que un producto inactivo no debe cobrarse y devuelve 0. Suena razonable —"¿por qué cobrar algo inactivo?"—, pero cambió el comportamiento del que el checkout depende: un item inactivo que ya está en el carrito de un cliente debe cobrarse al precio de siempre, no volverse gratis. La "limpieza" acaba de regalar productos. El golden master lo atrapó de inmediato: 13917 contra 0, una diferencia imposible de ignorar.kbd-mech: 96964 → 96971 ycable-hdmi: 35786 → 35787. Los dos son casos de descuento por volumen, y difieren por unos centavos porque la reimplementación redondeó el bulk de forma normal en vez de truncar a la decena de centavo como hace el legacy. Parecen diferencias minúsculas —siete centavos, un centavo—, pero son un cambio de precio real para el cliente, y si un socio de lealtad concilia contra esos números exactos, la diferencia rompe la conciliación.
Y fíjate en un detalle fino: usb-hub coincide (38558 en ambos), aunque también es un caso de volumen. El truncado del legacy y el redondeo de la candidata dieron, por casualidad, el mismo número para ese caso. Esto es importante: la rareza del bulk no se manifiesta en todos los casos de volumen, solo en algunos, según cómo caigan los centavos. Si hubieras caracterizado a mano y por suerte solo hubieras probado usb-hub, la reimplementación habría pasado tu prueba en verde y la regresión se habría ido a producción. El golden master, con sus varios casos de volumen, no te deja ese margen de suerte.
El primer paso del método quedó completo: elegiste la rebanada (catalog, la hoja del seam) y congelaste su comportamiento en un golden master que ya demostró su valor atrapando una regresión antes de que tocaras el código de producción. La red está puesta. Ahora sí se puede subir.
Profundización: por qué la hoja primero, y por qué la rareza se congela
Dos decisiones de este paso merecen desarrollarse, porque son las que sostienen todo lo que sigue.
Por qué el catalog primero: la hoja del seam. Cuando descompones un monolito, el orden de extracción importa, y la regla es empezar por el módulo con menos dependencias salientes —el que menos necesita de los demás—. La razón es mecánica: extraer un módulo significa cortar los hilos que lo unen al resto, y un módulo que depende de muchos otros tiene muchos hilos que cortar (y cada corte es riesgo). El catalog es la hoja porque los demás dependen de él, no al revés:
payments ──> orders ──> catalog
│ ▲
shipping ──────┘ │
(catalog no depende de nadie)
Dependencias SALIENTES por modulo:
catalog : 0 <- la hoja: se extrae con menos hilos que cortar
orders : 1 (depende de catalog)
shipping : 1 (depende de orders)
payments : 1 (depende de orders)
Extraer catalog primero no obliga a extraer nada más antes: como no depende de otros módulos, sacarlo no arrastra dependencias. Si empezaras por orders, tendrías que lidiar con su dependencia hacia catalog desde el primer día —más hilos, más riesgo, en el paso donde menos experiencia tienes—. La hoja primero es la regla del módulo 5: se empieza por donde hay menos que cortar, se gana experiencia con el patrón, y se avanza hacia los módulos más enredados con el método ya practicado.
Por qué la rareza se congela en vez de "arreglarse". La tentación más natural al reimplementar código viejo es limpiarlo: "este truncado raro es claramente un bug, lo hago bien"; "cobrar un producto inactivo no tiene sentido, lo quito". El golden master existe para frenar esa tentación en seco, porque un comportamiento que lleva años en producción casi nunca está solo: con suficientes consumidores, cada comportamiento observable se vuelve un contrato del que alguien depende, aunque nadie lo haya escrito. El truncado del bulk puede ser exactamente el número contra el que un socio de lealtad concilia su rebate; el cobro del inactivo puede ser lo que evita que un carrito con un item recién desactivado se rompa en el checkout. La regla del primer paso es dura y clara: caracterizar preserva el comportamiento, no lo mejora. Primero congelas lo que hay —bug aparente incluido—, y después, si de verdad quieres cambiar una rareza, lo haces como un cambio de comportamiento deliberado, revisado y aprobado (regrabando el golden master conscientemente), no como una "limpieza" silenciosa colada en una reimplementación. La foto se toma antes de tocar; mejorar viene después, y con permiso.
Errores comunes
Empezar por la rebanada más enredada "porque es la más importante". Qué pasa: el equipo decide modernizar payments u orders primero, porque son el corazón del negocio, y se topa desde el día uno con todas sus dependencias hacia el resto del monolito. Por qué pasa: "lo más importante primero" suena a buena priorización, y la importancia de negocio se confunde con el orden de extracción. Cómo detectarlo: el primer intento de extracción se atasca en dependencias —para sacar orders hay que lidiar con catalog, con payments, con shipping a la vez—, y el equipo se desanima antes de completar una sola rebanada. Cómo corregirlo: empieza por la hoja del seam —el módulo con menos dependencias salientes, el catalog—, no por el más importante. La primera extracción es donde aprendes el método con el riesgo más bajo; gastar esa primera vez en el módulo más enredado es aprender a nadar en la parte honda. La importancia de negocio decide qué se moderniza eventualmente; las dependencias deciden en qué orden.
"Limpiar" las rarezas al reimplementar, sin registrarlas primero. Qué pasa: al reescribir el catalog, el desarrollador "arregla" lo que parece un bug (el truncado raro, el cobro del inactivo) creyendo que mejora el código. Por qué pasa: las rarezas se ven como defectos, y limpiar código feo se siente como lo correcto. Cómo detectarlo: sin golden master, el cambio pasa desapercibido hasta que un consumidor se queja —el socio cuya conciliación se rompió, el checkout que empezó a regalar productos—. Cómo corregirlo: graba el golden master antes de tocar una línea, con las rarezas incluidas, y compara toda reimplementación contra él. El golden master convierte "arreglé un bug" en "cambié tres números, aquí están": si el cambio era deseado, lo apruebas conscientemente; si no —como el inactivo que se volvió gratis—, lo reviertes. La rareza no se limpia por instinto; se congela primero y se cambia después, con permiso.
Caracterizar a mano con pocos casos y creer que basta. Qué pasa: el equipo prueba tres o cuatro casos elegidos a mano, todos pasan, y da la caracterización por buena. Por qué pasa: unos pocos casos son rápidos de escribir y dan una sensación de cobertura. Cómo detectarlo: la rareza vive en combinaciones que no se probaron —como usb-hub, un caso de volumen que coincide aunque otros casos de volumen difieran—, así que una prueba a mano puede caer justo en los casos que no revelan el problema. Cómo corregirlo: usa varios casos que barran las rarezas a propósito (varios casos de volumen, no uno; el caso inactivo explícito), porque las rarezas no se manifiestan en todos los casos, solo en algunos. En el ejemplo, probar solo usb-hub habría dado verde en falso; fueron kbd-mech y cable-hdmi los que revelaron el truncado. Cubrir las rarezas con volumen suficiente es lo que hace que el golden master atrape lo que la intuición deja pasar.
Ejercicios
Ejercicio 1 — ¿Por qué el catalog primero? (a) ¿Qué significa que el catalog sea la "hoja" del grafo de dependencias? (b) ¿Qué problema aparecería si empezaras la modernización por orders? (c) ¿Qué criterio decide el orden de extracción, y en qué se diferencia del criterio que decide qué se moderniza eventualmente?
Ver solución
(a) Que el catalog tiene cero dependencias salientes: no necesita de ningún otro módulo del monolito para funcionar (orders, payments y shipping dependen de él, pero él no depende de ninguno). En el grafo de dependencias es una hoja —un nodo del que salen flechas hacia él, pero del que no sale ninguna hacia otros—. Por eso extraerlo no obliga a cortar hilos hacia otros módulos: es el que menos ata.
(b) Empezar por orders te enfrentaría, desde el primer día, a su dependencia hacia catalog (y a las de payments y shipping que dependen de orders). Tendrías que decidir qué hacer con esos hilos —¿extraer catalog también?, ¿poner un ACL hacia él?— en el paso donde menos experiencia tienes con el método. Más hilos que cortar significa más riesgo y más complejidad en la primera extracción, justo donde conviene que sea simple.
(c) El orden de extracción lo deciden las dependencias: se empieza por la hoja (menos dependencias salientes) y se avanza hacia los módulos más enredados con el método ya practicado. Qué se moderniza eventualmente lo decide la importancia de negocio (todo el monolito, a la larga). Los dos criterios son distintos: payments puede ser lo más importante, pero eso no lo hace lo primero —lo primero es lo que se extrae con menos riesgo, para aprender el patrón antes de aplicarlo a lo enredado—.
Ejercicio 2 — La limpieza que rompe. La reimplementación "limpia" cambió tres números respecto al golden master. (a) ¿Cuál de las tres diferencias es la más peligrosa para el negocio y por qué? (b) ¿Por qué usb-hub coincidió aunque también es un caso de volumen? (c) Si un desarrollador insiste en que el truncado del bulk "es un bug y hay que arreglarlo", ¿cuál es el procedimiento correcto?
Ver solución
(a) La más peligrosa es webcam-hd: 13917 → 0. Las diferencias del bulk son de centavos (importantes para una conciliación, pero acotadas); el inactivo que se vuelve gratis es un cambio de comportamiento grande y directo: cualquier producto inactivo que un cliente tenga en el carrito pasaría a costar 0, regalando inventario. Es la clase de "mejora limpia" que suena razonable en abstracto pero rompe un contrato real —el checkout depende de que los inactivos ya en el carrito se cobren al precio de siempre—.
(b) Porque la rareza del bulk (truncar a la decena de centavo vs. redondear normal) no se manifiesta en todos los casos de volumen, solo en aquellos donde los centavos caen de forma que el truncado y el redondeo difieren. Para usb-hub, por casualidad, los dos métodos dieron el mismo número (38558). Es exactamente la clase de coincidencia que hace la regresión escurridiza: probar un caso de volumen podría caer justo en uno que coincide, dando verde en falso.
(c) El procedimiento correcto: primero congelar, después cambiar con permiso. No "arreglar" el truncado colándolo en una reimplementación. Si de verdad el negocio decide que el bulk ahora debe redondear normal, eso es un cambio de comportamiento deliberado: se documenta, se revisa, se aprueba, y se regraba el golden master conscientemente al nuevo comportamiento —leyendo el diff, que es la lista literal de qué precios cambian y para quién—. Lo que no se hace es cambiar el número en silencio creyendo que se "mejora" código; esa es la regla dura del primer paso (caracterizar preserva, no mejora).
Ejercicio 3 — Diseña la red para otra rebanada. Te toca modernizar el módulo de shipping de Mercado, que calcula costos de envío con sus propias rarezas (un redondeo de peso hacia arriba, una tarifa mínima que se aplica raro). (a) ¿Qué grabarías en el golden master y cómo? (b) ¿Qué casos te asegurarías de incluir? (c) ¿Cómo usarías el golden master cuando reimplementes el cálculo?
Ver solución
(a) Grabaría la salida del shipping legacy para un conjunto de casos: por cada caso (peso, destino, tipo de servicio, valor del pedido), correría el cálculo legacy y guardaría el costo de envío exacto que devuelve —rarezas incluidas—. Ese registro (casos → costos) es el golden master, y lo guardaría versionado junto al código, como la foto del "antes" del shipping.
(b) Me aseguraría de incluir casos que barran las rarezas a propósito: varios pesos justo alrededor de los umbrales de redondeo (para exponer el redondeo hacia arriba en los casos donde importa), varios pedidos por debajo y por encima del monto de la tarifa mínima (para congelar cómo se aplica esa tarifa "rara"), y todos los tipos de servicio y varios destinos. Como en el catalog, la rareza no se manifiesta en todos los casos, así que un solo caso por rareza no basta —necesito volumen alrededor de los bordes—.
(c) Al reimplementar el cálculo, correría la nueva versión sobre los mismos casos y la compararía contra el golden master en un parallel-run. Si todos coinciden, el comportamiento se preservó (verde). Si algo difiere, el diff me diría exactamente qué caso cambió y cómo —y ahí decidiría: si el cambio era deseado (el negocio cambió una tarifa), regrabo el golden master conscientemente; si no (limpié una rareza de la que alguien depende), reviero mi reimplementación—. El golden master es la red que atrapa la regresión antes de que llegue a un cliente.
Resumen y siguiente paso
En esta lección diste el primer paso del método, que integra el módulo 1 (modernizar por rebanadas) y el módulo 2 (caracterizar el legacy). Elegiste la rebanada: el catalog, la hoja del seam —el módulo con cero dependencias salientes, el que se extrae con menos hilos que cortar—, y viste por qué el orden de extracción lo deciden las dependencias, no la importancia de negocio. Y pusiste la red antes de tocar nada: grabaste el golden master del catalog legacy —seis casos con sus rarezas congeladas (el truncado del descuento por volumen, el producto inactivo que se cobra igual)— y lo usaste para atrapar una reimplementación "limpia" que, con buena intención, redondeó el bulk de forma normal y dejó de cobrar los inactivos. El golden master atrapó las tres regresiones antes de que tocaran producción, y descubriste que la rareza no se manifiesta en todos los casos (usb-hub coincidió), por lo que caracterizar con volumen suficiente es lo que la atrapa con seguridad.
Antes de avanzar deberías poder: explicar por qué el catalog es la rebanada que conviene primero; grabar un golden master de una rebanada con sus rarezas incluidas; usarlo en un parallel-run para atrapar una regresión; y defender por qué caracterizar preserva el comportamiento en vez de mejorarlo.
La lección 3 da el segundo paso: poner la rebanada tras el facade. Con el golden master ya grabado como red, vas a interponer un strangler router delante del catalog y desviar el tráfico por porcentaje de 0 a 100, con la vieja ruta como fallback. Y vas a leer una revelación que conecta directo con esta lección: a traffic_percent = 100, el fallback sigue en 100 porque el modern todavía no sabe calcular el descuento por volumen —el mismo bulk cuyo golden master acabas de congelar—. El traffic_percent mide lo que intentaste; el fallback mide lo que el modern no pudo. La red de esta lección será justo lo que, más adelante, permita cerrar ese hueco.
Recursos
- Michael Feathers, Working Effectively with Legacy Code (Prentice Hall, 2004) — el libro que funda el primer paso del método: poner el código bajo characterization tests (congelar su comportamiento actual) antes de cambiarlo. Los capítulos sobre characterization tests y seams son la base directa de esta lección. En inglés.
- Sam Newman, Monolith to Microservices (O'Reilly, 2019), cap. 3 — sobre elegir qué extraer primero por el grafo de dependencias (empezar por lo que menos depende de otros), y el orden de descomposición de un monolito. La fuente de "la hoja primero". En inglés.
- Martin Fowler, "Patterns of Legacy Displacement" — martinfowler.com/articles/patterns-legacy-displacement. Correr la implementación vieja y la candidata sobre las mismas entradas y comparar sus salidas; el parallel run como patrón nombrado es de Sam Newman (Monolith to Microservices). El mecanismo exacto con que el golden master atrapa la regresión de esta lección. En inglés.
- Emily Bache, "Approval Testing" y ApprovalTests — approvaltests.com. El golden master llevado a práctica: aprobar una salida de referencia y compararla en cada corrida, leyendo el diff antes de re-aprobar. En inglés.