Módulo 8: Proyecto capstone — sé el arquitecto de Mercado ante un cambio
4. Aplica la maniobra inversa de Conway
Descripción
Este es el paso 3 del entregable, y es donde el atributo rector se convierte en organización. En la lección 3 derivaste que scalability es lo que este cambio debe producir —un surface de vendedores que escale independientemente—. Pero un atributo no se logra dibujándolo: se logra estructurando los equipos que lo producen. Al terminar esta lección vas a tener el tercer artefacto del dossier —el mapa org↔arquitectura ejecutado— y vas a haber medido, en Python, cuánto baja la fricción de coordinación del sistema al aplicar la maniobra inversa de Conway al surface de vendedores, sin tocar una línea de código. Este es el paso que traduce "queremos que el surface escale" en "creamos un equipo que lo posea de punta a punta", que es la única forma de que ese surface de verdad escale.
Esto importa porque el error más caro al meter un cambio grande es dejar que la Ley de Conway opere en contra. Un cambio como "abrir a vendedores externos" trae un surface nuevo —la Seller API, el onboarding, la ingesta de listings, los payouts— que, si nadie lo posee, se reparte por proximidad entre las squads existentes: platform y orders se disputan la API, catalog y platform se disputan la ingesta, payments y platform se disputan los payouts. Ese surface co-poseído por varios equipos no puede escalar, porque cada cambio exige coordinar a dos o tres squads —exactamente lo contrario de lo que scalability pide—. La maniobra inversa da vuelta la secuencia: en vez de dibujar tres servicios independientes y rezar para que los equipos respeten las fronteras, diseñas la organización que producirá esos servicios —un equipo stream-aligned dueño del surface completo— y los servicios independientes salen casi solos, porque la fuerza de Conway ahora empuja en la dirección que quieres. Primero la organización, después —y casi sola— la arquitectura.
Conexión con el módulo: esta lección hace el paso 3 del hilo y aporta la pieza de M2 al capstone. Recibe su entrada de la lección 3 (el atributo rector: scalability) y de la lección 2 (el mapa de ownership: las 38 decisiones locales que se delegan necesitan equipos con fronteras claras que las absorban). Su salida —la decisión estructural: crear un equipo seller_platform y volver la plataforma un servicio— es exactamente lo que la lección 5 comunicará con el C4 y el ADR. La frontera del módulo se respeta: aquí diseñas la dinámica org↔arquitectura y la mides; la reestructuración detallada de equipos como problema de management (a quién contratar, cómo transferir personas) queda fuera. Aquí el objeto es la palanca —cómo mover la organización mueve el sistema—, medida.
Para cambiar el cauce del río, no empujes el agua
Un río baja por una ladera y, al llegar al valle, hace una curva cerrada que inunda un sembradío cada temporada de lluvias. Quieres que el agua pase derecho. Tienes dos formas de intentarlo.
La primera, la ingenua: empujar el agua. Pones costales de arena, desvías el flujo a mano, cavas un canalito para guiar la corriente. Funciona unos días. En cuanto llueve fuerte, el agua vuelve a su curva de siempre, tira los costales y reinunda el sembradío. Peleas contra el agua cada temporada, para siempre, y siempre pierdes —porque el agua no sigue tus costales, sigue la forma del terreno—.
La segunda, la que funciona: cambiar el terreno. En vez de empujar el agua, excavas un cauce nuevo, recto, y bloqueas el viejo con un terraplén. Ahora el agua pasa derecho no porque la estés empujando, sino porque el terreno la lleva ahí naturalmente. No tienes que hacer nada cada temporada; el río fluye recto solo. Un trabajo grande una vez, en vez de una pelea chica para siempre.
El código es el agua; la organización es el terreno. Meter el surface de vendedores en el sistema sin darle un equipo dueño —dejando que catalog, orders, payments y platform se lo repartan— es empujar el agua: aunque dibujes "la Seller API es un servicio independiente", el código volverá a acoplarse con todo, porque los cuatro equipos que lo tocan seguirán compartiendo y coordinando, y el surface reflejará esa comunicación, no tu diagrama. La maniobra inversa de Conway es cambiar el terreno: creas un equipo dueño del surface completo (excavas el cauce nuevo) y el surface fluye hacia un servicio independiente, solo, porque ahora la estructura de comunicación lo lleva ahí. Esta lección mide cuánto endereza el río cambiar el terreno.
Ejemplo trabajado: la maniobra inversa, medida sobre el cambio
Vamos a modelar el sistema de Mercado con los cuatro módulos nuevos que trae el cambio (seller_api, seller_onboarding, listing_ingestion, payout_processing) y a medir la fricción de coordinación de dos organizaciones: la trampa (el surface repartido por proximidad, sin dueño) y la maniobra inversa (un equipo stream-aligned dueño del surface completo, más la plataforma como servicio). La fricción de un módulo es C(k,2) —cuántos pares de equipos tienen que coordinar por él—; una dependencia hacia un servicio de plataforma no mete a ese equipo en la sala, porque se consume por contrato estable, no por co-cambio.
# Capstone paso 3: la maniobra INVERSA de Conway aplicada al cambio.
# Para OBTENER la arquitectura que el cambio necesita (un surface de vendedores
# externos, independiente y escalable), diseniamos la ORGANIZACION que la produciria,
# y medimos la friccion de coordinacion antes/despues -- sin tocar el codigo primero.
from itertools import combinations
# Modulos del sistema, incluidos los 4 NUEVOS que trae el cambio (seller surface).
deps = {
"search": ["product_catalog"],
"cart": ["product_catalog"],
"checkout": ["cart", "payment_processing", "shipping_labels",
"notifications", "order_processing"],
"order_processing": ["shipping_labels", "auth"],
"payment_processing": ["invoicing", "auth"],
"invoicing": ["notifications"],
"shipping_labels": ["delivery_tracking"],
# --- nuevo surface de vendedores externos ---
"seller_api": ["seller_onboarding", "listing_ingestion",
"payout_processing", "auth"],
"seller_onboarding": ["auth", "notifications"],
"listing_ingestion": ["product_catalog", "search"],
"payout_processing": ["payment_processing", "invoicing"],
}
modules = ["product_catalog", "search", "cart", "order_processing", "checkout",
"payment_processing", "invoicing", "shipping_labels", "delivery_tracking",
"auth", "notifications",
"seller_api", "seller_onboarding", "listing_ingestion", "payout_processing"]
def total_friction(owners, as_a_service):
"""Suma de C(k,2) sobre todos los modulos: cuantos pares de equipos tienen que
coordinar por cada modulo. Una dependencia hacia un servicio de plataforma
(as_a_service) NO mete a ese equipo en la sala: se consume por contrato estable."""
total = 0
for m in modules:
room = set(owners[m])
for d in deps.get(m, []):
if d not in as_a_service:
room |= owners[d]
total += len(list(combinations(room, 2)))
return total
# ANTES (la trampa): el surface nuevo llega SIN duenio claro y se reparte por
# proximidad entre las squads existentes -> co-propiedad y co-cambio en todas partes.
before_owners = {
"product_catalog": {"catalog"}, "search": {"catalog"},
"cart": {"orders"}, "order_processing": {"orders"},
"checkout": {"orders", "payments"},
"payment_processing": {"payments"}, "invoicing": {"payments"},
"shipping_labels": {"shipping"}, "delivery_tracking": {"shipping"},
"auth": {"platform"}, "notifications": {"platform"},
"seller_api": {"platform", "orders"}, # nadie lo posee: dos squads se lo reparten
"seller_onboarding": {"platform", "orders"},
"listing_ingestion": {"catalog", "platform"},
"payout_processing": {"payments", "platform"},
}
before_service = set() # nada es servicio: todo se co-cambia
# DESPUES (maniobra inversa): un equipo stream-aligned 'seller_platform' que posee el
# surface de vendedores COMPLETO (un solo duenio); y auth + notifications + payment
# se vuelven servicios de plataforma con contrato estable (se consumen, no se co-cambian).
after_owners = {
"product_catalog": {"catalog"}, "search": {"catalog"},
"cart": {"orders"}, "order_processing": {"orders"},
"checkout": {"orders", "payments"},
"payment_processing": {"payments"}, "invoicing": {"payments"},
"shipping_labels": {"shipping"}, "delivery_tracking": {"shipping"},
"auth": {"platform"}, "notifications": {"platform"},
"seller_api": {"seller_platform"},
"seller_onboarding": {"seller_platform"},
"listing_ingestion": {"seller_platform"},
"payout_processing": {"seller_platform"},
}
after_service = {"auth", "notifications", "payment_processing"}
fb = total_friction(before_owners, before_service)
fa = total_friction(after_owners, after_service)
def seller_api_pairs(owners, as_a_service):
room = set(owners["seller_api"])
for d in deps["seller_api"]:
if d not in as_a_service:
room |= owners[d]
return len(list(combinations(room, 2)))
print(f"{'escenario':<40}{'#duenos seller_api':>19}{'friccion total':>16}")
print(f"{'ANTES (surface repartido, sin duenio)':<40}"
f"{len(before_owners['seller_api']):>19}{fb:>16}")
print(f"{'DESPUES (maniobra inversa: seller_platform)':<40}"
f"{len(after_owners['seller_api']):>19}{fa:>16}")
print()
print(f"friccion de seller_api : {seller_api_pairs(before_owners, before_service)}"
f" -> {seller_api_pairs(after_owners, after_service)} pares")
print(f"friccion total : {fb} -> {fa}"
f" (baja {(1 - fa / fb) * 100:.0f}%)")
# Communication paths: el equipo nuevo mantiene su tamanio bajo control.
def paths(n):
return n * (n - 1) // 2
print()
print(f"seller_platform de 6 personas -> {paths(6)} canales internos (chico, rapido).")
print("No tocamos el codigo primero: creamos el EQUIPO que producira la arquitectura.")
Qué esperar. Al correrlo:
escenario #duenos seller_api friccion total
ANTES (surface repartido, sin duenio) 2 21
DESPUES (maniobra inversa: seller_platform) 1 7
friccion de seller_api : 6 -> 0 pares
friccion total : 21 -> 7 (baja 67%)
seller_platform de 6 personas -> 15 canales internos (chico, rapido).
No tocamos el codigo primero: creamos el EQUIPO que producira la arquitectura.
Detente en los números, porque son el atributo rector hecho estructura.
La fricción total cayó de 21 a 7 —un 67%— sin tocar el código. El mismo mapa de módulos, las mismas dependencias, byte por byte igual. Lo único que cambió fue la organización: un dueño único para el surface de vendedores, y la plataforma vuelta servicio. Con ese solo cambio, dos tercios de la fricción de coordinación desaparecieron. Esto es la maniobra inversa en acción: no rediseñamos el sistema, rediseñamos la organización, y el sistema —medido por su fricción— mejoró como si lo hubiéramos rediseñado. Cambiamos el terreno, y el río se enderezó. Y fíjate que esta mejora es exactamente lo que scalability necesita: un surface con dos tercios menos de fricción de coordinación es un surface que puede cambiar y escalar rápido, porque cada ajuste ya no arrastra a tres squads a una reunión.
El seller_api bajó de 6 a 0 pares de coordinación, y de 2 dueños a 1. El módulo que iba a concentrar la peor fricción —la API pública, que en la trampa se la repartían platform y orders y que dependía de piezas esparcidas por cuatro equipos— quedó completamente interno a un solo equipo. En la trampa, cambiar la Seller API obligaba a coordinar 6 pares de equipos; tras la maniobra, el equipo seller_platform la cambia solo, sin meter a nadie más en la sala, porque posee todas sus dependencias directas (onboarding, ingesta, payouts) y consume auth como servicio. Ese es el corazón de un equipo stream-aligned: dueño del flujo de valor completo, autónomo para cambiarlo. Un surface de vendedores que se puede cambiar sin coordinar con nadie es, por definición, un surface que escala.
Aquí está la letra chica honesta, y es importante para el ADR que escribirás después. La fricción total no cayó a cero: se quedó en 7, y ese 7 es coordinación genuina, no artificial. Al desglosarlo, dos de esos pares son las costuras reales del surface de vendedores con el resto del negocio: listing_ingestion sigue coordinando con catalog (importar los productos de los vendedores externos escribe en el catálogo —es una dependencia real—) y payout_processing sigue coordinando con payments (pagarle a los vendedores usa el motor de pagos —también real—). La maniobra no pretende abolir esas costuras, porque son la esencia del negocio: un surface de vendedores de verdad tiene que meter productos al catálogo y pagarle a la gente. Lo que la maniobra elimina es la fricción artificial de la mala organización (los cuatro equipos disputándose la API); lo que deja es la fricción irreducible (las dos costuras que el negocio realmente requiere), y las deja fluir por el canal más barato posible —un contrato estable, no una cocina compartida—. Esas dos costuras son justo lo que el ADR de la lección 5 nombrará como "las dos costuras que exigen contratos estables", y lo que el rollout de la lección 6 tendrá que hacer que las squads adopten.
Visto como topología de equipos, el antes y el después son estos:
flowchart TB
subgraph ANTES["ANTES: el surface repartido (empujar el agua)"]
direction LR
p1["platform + orders<br/>seller_api, onboarding"]
c1["catalog + platform<br/>listing_ingestion"]
pay1["payments + platform<br/>payout_processing"]
p1 <--> c1
c1 <--> pay1
p1 <--> pay1
end
subgraph DESPUES["DESPUES: un equipo dueno (cambiar el terreno)"]
direction LR
sp["seller_platform<br/>(stream-aligned)<br/>posee TODO el surface"]
cat["catalog"]
paym["payments"]
plat["platform<br/>(auth, notifications<br/>como servicio)"]
sp -->|contrato: import listings| cat
sp -->|contrato: payouts| paym
sp -.->|consume as-a-service| plat
end
El diagrama de la izquierda es la trampa: tres regiones borrosas donde varios equipos se disputan cada pieza del surface, con coordinación en todas direcciones (la fricción 21). El de la derecha es la maniobra: un equipo stream-aligned que posee el surface completo, con solo dos costuras genuinas hacia catalog y payments (por contrato) y el resto consumido como servicio (la fricción 7). Un dueño donde había disputa.
Profundización: por qué esta pieza depende de las dos anteriores
La maniobra inversa es poderosa, y como toda herramienta poderosa, mal usada hace daño. En el capstone su uso correcto depende de que los pasos 1 y 2 se hayan hecho bien —y ahí está la lección de integración—.
Depende del paso 2 (el atributo rector), que le da la dirección. La maniobra inversa diseña la organización a partir de la arquitectura deseada, y la arquitectura deseada la dicta el atributo rector. Aquí el rector es scalability, así que la organización se diseña para producir un surface que escala —un equipo stream-aligned dueño del flujo, autónomo—. Si el rector hubiera sido security (como en el proyecto de pagos a plazos), la maniobra habría diseñado otra organización —quizás una capacidad de plataforma que provee controles de seguridad, o fronteras alrededor del aislamiento de datos—. Aplicar la maniobra sin haber derivado el atributo rector es reorganizar a ciegas: mover equipos sin saber qué arquitectura deben producir, el anti-patrón de las "reorganizaciones perpetuas" que dejan a todos mareados y a la arquitectura igual. El paso 3 necesita la salida del paso 2 para no ser un ritual.
Depende del paso 1 (el rol enmarcado), que hace que la maniobra sea el remedio correcto al embudo. En la lección 2 viste que el arquitecto no puede ser el canal único de las 46 decisiones, y que la salida era "estructurar los equipos para que las 38 locales fluyan sin él". La maniobra inversa es exactamente esa estructuración: crear un equipo que posea el surface de vendedores es lo que hace que las decisiones locales de ese surface (cómo se implementa la ingesta, cómo se estructura el onboarding) las tome ese equipo, no el arquitecto. La fricción que la maniobra reduce (21→7) es la misma coordinación que, si no se redujera, tendría que pasar por el escritorio del arquitecto-embudo. Los dos pasos son las dos caras de la defensa contra el bottleneck: el paso 1 decide qué no es del arquitecto, el paso 3 crea los equipos que lo absorben.
Y tres criterios de cuándo la maniobra está justificada, para no operar a un paciente sano:
Primero, hay un desalineamiento medido y una arquitectura objetivo clara. Aquí los dos se cumplen: la fricción evitable es alta (21 con el surface repartido) y la arquitectura objetivo es nítida (un surface de vendedores independiente, que scalability exige). Sin objetivo claro, reorganizar es agitar el organigrama y esperar suerte.
Segundo, la maniobra deriva la organización de la arquitectura, no al revés. El orden correcto: (1) el atributo rector dice qué arquitectura quieres (un surface que escala); (2) diseñas la organización que la produce (un equipo dueño del surface); (3) reorganizas hacia ella; (4) dejas que el código fluya. El error es reorganizar "porque tocaba" y ver qué sale.
Tercero, la maniobra no elimina la coordinación necesaria, solo la artificial. La fricción bajó a 7, no a 0, porque las dos costuras (import listings ↔ catalog, payouts ↔ payments) son reales. Forzar fronteras artificiales para eliminarlas —partir el surface de su necesidad de escribir en el catálogo— rompería el negocio. La maniobra bien hecha reconoce la coordinación irreducible y la deja fluir por contratos estables, en vez de pretender abolirla.
Un matiz honesto sobre el costo. El modelo trata "volver un servicio" y "crear un equipo" como interruptores, y en la realidad son trabajo: definir los contratos, contratar y montar el equipo seller_platform, transferir conocimiento, aguantar unos meses de transición. El 67% es el destino, no el primer día. Lo que el número prueba no es que la reorganización sea gratis, sino que es la palanca correcta: el mismo esfuerzo invertido en reorganizar rinde una mejora que se sostiene (porque la organización deja de empujar en contra), mientras que invertido solo en reescribir código rinde una mejora que se erosiona. Ese costo y esa transición son, de hecho, parte de lo que el plan de evolución de la lección 7 va a secuenciar.
Errores comunes
Meter el surface nuevo sin darle dueño (de reorganización que no ocurre). Qué pasa: el cambio agrega la Seller API y sus piezas, pero nadie decide quién las posee, así que se reparten por proximidad entre las squads que "quedan cerca" —y el sistema nace con la fricción 21—. Por qué pasa: crear un equipo nuevo se siente como un problema de management "para después", mientras que meter el código se siente urgente. Cómo detectarlo: si algún módulo del surface tiene dos o más dueños en tu mapa, no aplicaste la maniobra —dejaste que el accidente organizacional dictara la estructura—. Cómo corregirlo: antes de escribir el surface, decide qué equipo lo poseerá completo; el dueño único es lo que hace que la fricción sea 7 en vez de 21, y es una decisión de arquitecto (cross-team), no de management "para después".
Rediseñar el código del surface sin cambiar la organización (de empujar el agua). Qué pasa: el equipo separa limpiamente la Seller API en el código, celebra, y en seis meses está tan acoplada con catalog, orders y payments como antes, porque los cuatro equipos siguen tocándola. Por qué pasa: reescribir se siente como progreso tangible; reorganizar se siente político. Cómo detectarlo: si separaste módulos pero no cambiaste quién los posee, empujaste el agua con costales. Cómo corregirlo: invierte la secuencia —primero el equipo dueño (excava el cauce), después el código fluye y se mantiene—; la maniobra existe precisamente para que el refactor no se erosione.
Esperar que la maniobra lleve la fricción a cero (de negar lo irreducible). Qué pasa: el arquitecto ve que la fricción bajó a 7 y se frustra, o peor, fuerza fronteras artificiales para eliminar las dos costuras genuinas —separando el surface de su necesidad de escribir en el catálogo o de pagar por payments—, y rompe el negocio. Por qué pasa: se confunde "reducir la fricción artificial" con "abolir toda coordinación". Cómo detectarlo: si estás partiendo dependencias que son la esencia del negocio (un surface de vendedores tiene que meter productos y pagar), estás negando lo irreducible. Cómo corregirlo: acepta que las costuras reales (import listings ↔ catalog, payouts ↔ payments) se quedan, y hazlas fluir por el canal más barato —un contrato estable—; esas dos son justo lo que el ADR nombrará como el precio consciente y lo que el rollout tendrá que gestionar.
Ejercicios
Ejercicio 1 — Diseña la organización desde el atributo. El atributo rector de este cambio es scalability, y por eso la maniobra creó un equipo stream-aligned dueño del surface. Supón que, a mitad del cambio, el liderazgo agrega una meta que dispara security como co-rector (por ejemplo, "cumplir una certificación de seguridad para poder integrar bancos como vendedores"). ¿Cómo cambiaría la organización que diseñas? Piensa en qué equipo o capacidad nueva aparecería.
Ver solución
Con security como co-rector, además del equipo stream-aligned aparecería una capacidad de plataforma de seguridad. El equipo seller_platform (stream-aligned, dueño del surface) sigue teniendo sentido porque scalability no desapareció. Pero security al mismo nivel pide algo que un equipo de flujo no provee bien por sí solo: controles de seguridad consistentes y de alto nivel de expertise (auth de terceros robusta, auditoría, aislamiento de datos, cumplimiento de la certificación). Eso apunta a uno de dos patrones de Team Topologies:
-
Un equipo platform que provee la seguridad como servicio —auth de terceros, gestión de secretos, auditoría— que seller_platform (y las demás squads) consumen por contrato, en vez de que cada equipo implemente su propia seguridad de forma inconsistente. Esto encaja con el "auth como servicio" que la maniobra ya introdujo, ahora reforzado.
-
Un equipo enabling o complicated-subsystem temporal que ayude a seller_platform a alcanzar el nivel de seguridad de la certificación —porque el cumplimiento de una certificación bancaria requiere una expertise que el equipo de flujo no tiene y no debería tener que desarrollar desde cero—.
La lección de integración: la organización que diseñas es fiel al conjunto de atributos rectores. Un solo rector (scalability) produjo un solo equipo stream-aligned; dos rectores (scalability + security) producen ese equipo más una capacidad de plataforma o enabling que provee el segundo atributo sin que el equipo de flujo cargue con toda su complejidad. Esto muestra por qué el paso 2 (derivar los atributos) tiene que venir antes del paso 3: la estructura se deriva de qué atributos hay que producir, y agregar un atributo agrega una pieza a la organización.
Ejercicio 2 — El refactor que se erosiona. Un lead, sin esperar a que se forme el equipo seller_platform, dedica un mes a separar limpiamente en el código la Seller API del monolito, con fronteras impecables. Al terminar, el código está separado, pero el equipo aún no existe: catalog, orders y payments siguen tocando el surface. Predice, con la Ley de Conway, qué pasará en los siguientes seis meses, y qué debió hacerse distinto.
Ver solución
Qué va a pasar: el surface impecablemente separado se va a re-acoplar. Como catalog, orders y payments siguen tocándolo (la organización no cambió), esos equipos van a seguir coordinando sobre esa área, y cada vez que coordinen bajo presión —un bug urgente, una feature con deadline— meterán el atajo más rápido: una llamada directa a un módulo interno del surface, un dato compartido "temporal", una dependencia cruzada "solo por ahora". En seis meses, la Seller API "separada" estará tan entrelazada con el resto como si nunca se hubiera tocado. Es empujar el agua con costales: el refactor se erosiona porque el terreno de la comunicación (cuatro equipos tocando el surface) no cambió, y el código vuelve a fluir hacia la forma que ese terreno dicta. La fricción medida seguiría cerca de 21, no de 7.
Qué debió hacerse distinto: aplicar la maniobra inversa antes o junto con el refactor —formar el equipo seller_platform (o al menos asignar el ownership completo del surface a un equipo) y solo entonces separar el código—. Con un dueño único, la separación se sostiene: no hay tres equipos empujando dependencias cruzadas en contra, así que la frontera que el equipo dueño mantenga no la erosiona nadie desde afuera. El mes de refactor no fue inútil, pero fue el segundo paso ejecutado como si fuera el primero. La regla del capstone: la estructura organizacional (paso 3) habilita al código; sin ella, el código no se sostiene, por más limpio que nazca. Primero el equipo, después —y de forma duradera— el servicio independiente.
Ejercicio 3 — ¿Qué costura es genuina y cuál es artificial? La maniobra bajó la fricción a 7, no a 0, porque dos costuras son irreducibles (import listings ↔ catalog; payouts ↔ payments). Para cada una de estas tres dependencias que alguien propone "eliminar reorganizando", decide si es coordinación genuina (déjala, hazla contrato) o artificial (elimínala con la maniobra), y por qué: (a) seller_platform tiene que escribir los productos de los vendedores en el catálogo; (b) seller_platform y platform se disputan quién mantiene el código de la Seller API; (c) el payout a un vendedor tiene que pasar por el motor de pagos de la squad payments.
Ver solución
-
(a) seller_platform escribe en el catálogo → coordinación GENUINA. Un surface de vendedores externos existe para que sus productos aparezcan en el marketplace, y el marketplace es el catálogo. Escribir los listings en el catálogo es el negocio; no hay forma de "eliminar" esa dependencia sin eliminar la razón de ser del surface. La maniobra correcta no la abole: la vuelve un contrato estable (una API de ingesta que catalog expone y seller_platform consume), para que la coordinación sea barata en vez de una cocina compartida. Es una de las dos costuras irreducibles que el ADR nombra. Déjala, hazla contrato.
-
(b) seller_platform y platform se disputan la Seller API → coordinación ARTIFICIAL. Que dos equipos se disputen quién mantiene la Seller API no es una necesidad del negocio: es un accidente de la mala asignación de ownership (la trampa del "antes"). Esta es exactamente la fricción que la maniobra elimina: la Seller API la posee un equipo (seller_platform), punto. Cero pares de coordinación por ella (el
seller_api: 6 → 0del ejercicio trabajado). Elimínala con la maniobra dándole dueño único. -
(c) el payout pasa por el motor de pagos de payments → coordinación GENUINA. Pagarle a los vendedores externos implica mover dinero real, y el motor de pagos (con su cumplimiento, su integración con el gateway, su auditoría) vive en payments. seller_platform no debería reimplementar un motor de pagos —eso duplicaría una capacidad crítica y regulada—. La dependencia es real: la coordinación correcta es un contrato (payments expone una API de payout que seller_platform consume), no fusionar los equipos ni que seller_platform se meta al código de pagos. Es la segunda costura irreducible. Déjala, hazla contrato.
El patrón: la coordinación artificial viene de que varios equipos comparten la propiedad de un mismo componente (se elimina dando dueño único); la coordinación genuina viene de que dos capacidades distintas del negocio de verdad se necesitan (se conserva, pero se abarata con un contrato estable). La maniobra bien hecha distingue las dos: elimina (b), conserva-como-contrato (a) y (c). Confundirlas es el error —abolir una costura genuina rompe el negocio; conservar una artificial deja la fricción 21—.
Resumen y siguiente paso
En esta lección hiciste el paso 3 del entregable: convertir el atributo rector en organización con la maniobra inversa de Conway. Con el río y el cauce entendiste que empujar el agua (rediseñar el código) es una pelea que se pierde cada temporada, mientras que cambiar el terreno (crear el equipo dueño) endereza el río solo. Lo mediste ejecutando: el surface de vendedores repartido por proximidad produce una fricción de 21 pares; darle un equipo stream-aligned dueño del surface completo y volver la plataforma un servicio la baja a 7 (−67%) sin tocar el código, y el seller_api pasa de 6 pares de coordinación a 0 —completamente interno a un equipo, que es lo que scalability exige—. Entendiste que la fricción no baja a cero porque dos costuras (import listings ↔ catalog, payouts ↔ payments) son irreducibles y se vuelven contratos, no se abolen; y que esta pieza depende de las dos anteriores —el atributo rector le da la dirección, el rol enmarcado hace que sea el remedio al embudo—.
Antes de avanzar deberías poder: aplicar la maniobra inversa —derivar la organización que produciría la arquitectura que el atributo rector exige—; medir el antes y el después de la fricción; distinguir la coordinación genuina (se hace contrato) de la artificial (se elimina con dueño único); y explicar por qué reorganizar el código sin la organización se erosiona.
Lo que sigue es el paso 4: comunicar esta decisión. Ya decidiste la estructura —crear seller_platform, volver la plataforma un servicio, con dos costuras por contrato—; la lección 5 te enseña a comunicarla a cada audiencia. Vas a dibujar el C4 del cambio en mermaid —el Context para el VP (con el vendedor externo que ahora integra por API) y el Container para el dev (con el gateway nuevo y el servicio seller_platform)— y a redactar el ADR-021 completo que empaqueta el porqué de esta decisión para el futuro. El diagrama comunica el qué que acabas de decidir; el ADR comunica el por qué —incluidas las dos costuras irreducibles como el precio consciente—.
Recursos
- Skelton & Pais — Team Topologies (Inverse Conway Maneuver y los cuatro tipos de equipo) — el libro que nombró la maniobra y define el equipo stream-aligned (dueño de un flujo de valor) que aquí creaste para el surface de vendedores; la fuente directa del paso 3.
- Martin Fowler — "Conway's Law" — la formulación de por qué "primero se cambia la organización" ("if the architecture of the system and the architecture of the organization are at odds, the architecture of the organization wins"), el principio que la maniobra explota.
- ThoughtWorks Technology Radar — "Inverse Conway Maneuver" — la ficha que trata la maniobra como técnica de ingeniería con sus advertencias (cuándo aplicarla y cuándo es cirugía innecesaria), útil para el criterio de coordinación genuina vs artificial.
- Melvin Conway — "How Do Committees Invent?" (1968) — el artículo original que formuló la ley que hace posible la maniobra; leerlo cierra el porqué de que la estructura del sistema copie la de la organización.