Module 2: Conway's Law
8. Project: redesign Mercado's organization for a new capability
Overview
This is your graduation from the module. Over seven lessons you learned to read an organization and predict the architecture it produces, to measure the org↔code mirror, to compute the coordination cost, to diagnose the module that crosses boundaries, to apply the inverse Conway maneuver, and to design with the team topologies catalog. You saw all that applied to one situation: Mercado's monolith and its shared checkout. Now it's your turn, from scratch, on a different situation —a new capability we didn't analyze in the lessons—. The reason for changing the case is the usual one and it's tough: if I let you re-fix the checkout of the lessons, I wouldn't know whether you learned the method or memorized the answer. With a new case, the only way to solve it is to apply the method —and that is, exactly, the proof that the module worked—.
Your deliverable is three artifacts for the new capability: (1) the org↔architecture map of the initial proposal —who owns what and what depends on what—; (2) the executed measurement of its friction and its per-team load, with Python; and (3) the inverse maneuver that fixes it —the organizational redesign using team topologies, with the friction measured before and after—. No part requires building the system: it's pure organizational design work —map, measure, redesign—, which is exactly what separates an architect who understands Conway from one who draws boxes. Build it yourself first; reading the reference solution without having attempted it is like reading the score of a game you didn't play.
Connection with the module: this project closes the arc. Lessons 2 to 5 gave you the diagnosis (the law, the monolith, the cost, the friction); lessons 6 and 7 gave you the cure (the inverse maneuver, the team topologies). Here you produce the three artifacts with your own hands, from beginning to end, on a new case. And with this lesson the module closes: at the end is the summary of the eight lessons and the bridge to module 3, where you'll learn to communicate these designs —the C4 model and the ADR as tools for the organization to understand and adopt the restructuring you now know how to design—.
The project case: Mercado launches reviews and recommendations
The new situation —yours to solve— is this:
Mercado wants to launch two capabilities at once: reviews (customers leaving reviews and ratings of products, with content moderation) and recommendations (recommending products to each customer with a machine learning model). Leadership, in a hurry to launch, decides to create a single new squad,
growth, and give it all the capability: the five new modules. Your job as architect is to evaluate that decision with the module's method and propose a better organization if needed.
The new modules and what they depend on (given to you by the team, so you don't have to invent it):
reviews_api— receives and stores the reviews. Depends onmoderation(to filter content), onproduct_catalog(to associate the review with the product, it'scatalog's), onauth(to know who reviews, it'splatform's), and onnotifications(to notify, it'splatform's).moderation— filters offensive reviews with a model. Depends onml_training(uses the trained model).rating_aggregate— computes each product's average rating. Depends onreviews_api.recommendations— recommends products. Depends onrating_aggregate, onml_training, and onproduct_catalog(catalog's).ml_training— trains the machine learning models thatmoderationandrecommendationsuse.
Notice the mix: reviews (reviews, moderation, ratings) is a user-facing capability, close to the product; recommendations + ml_training is a machine learning capability, which requires deep and different expertise. Leadership put the two in a single squad. Your module's method should have something to say about that. Don't re-teach how an ML model or a reviews API works —that's for other guides—; your job is to design the organization that produces the best architecture for this capability.
What you have to deliver
Follow the steps in order; each one leans on the previous.
Part 1 — The org↔architecture map of the initial proposal
Draw (in text or ASCII) the map of leadership's proposal: the growth squad owning the five new modules, catalog owning product_catalog, platform owning auth and notifications, and the dependencies among all. Identify by eye what worries you about that organization —before measuring—.
Part 2 — The executed measurement
Write and run the Python that measures, for the initial proposal, two things: the total friction (the pairs of teams that must coordinate per module, C(k,2), as in lessons 5 and 6) and the per-team load (how many modules the most loaded team owns). Deliver the real numbers, run, not cited from memory. Reuse the module's formulas.
Part 3 — The inverse maneuver
Redesign the organization using the team topologies catalog (lesson 7). Decide what teams there should be, what type each one is, and who owns what. Measure the friction and the load again with the new organization, and compare. Close by explaining what problem you solved and what coordination is irreducible.
The rubric
Here's how the project is evaluated. It's not by length or elegance: it's by whether the organizational design is well done and well defended with evidence.
| Criterion | Doesn't meet | Meets | Excels |
|---|---|---|---|
| Org↔architecture map | Only draws modules, without ownership | Ownership + clear dependencies | Also flags the risks by eye before measuring |
| Executed measurement | Numbers cited from memory or invented | Friction and load run in Python | Also states assumptions (what's a service, what co-changes) |
| Inverse maneuver | "Let's do microservices" without teams | Redesign with team topology types and ownership | Also measures the before/after and names the irreducible coordination |
| Judgment | Reorganizes without justifying | Justifies with the measured misalignment | Also says when it would NOT be worth reorganizing |
The criterion that weighs the most, and the one that separates an architect from a box-drawer, is the executed measurement: if you deliver everything else but the numbers came out of your head and not from running the code, you didn't measure —you asserted with invented figures, which is worse, because it fakes rigor—.
The reference solution
Attempt the whole project before continuing. What follows is one correct solution, not the only one.
Part 1 — The map of the initial proposal
Leadership's proposal, in a map:
growth (a single squad, owner of EVERYTHING new)
+----------------------------------------------------------------+
| reviews_api moderation rating_aggregate |
| recommendations ml_training |
+----------------------------------------------------------------+
| | | |
v v v v
product_catalog auth/notifications (platform) ...
dependencies:
reviews_api -> moderation, product_catalog, auth, notifications
moderation -> ml_training
rating_aggregate -> reviews_api
recommendations -> rating_aggregate, ml_training, product_catalog
What worries me by eye, before measuring: the growth squad owns five modules that span two very different capabilities: reviews (user-facing: reviews_api, moderation, rating_aggregate) and machine learning (recommendations, ml_training). This smells of two module problems: (1) a team with too much cognitive load —five modules from two different domains is a lot for a single squad (lesson 4)—; and (2) the absence of a boundary between two subsystems that should be separated —the ML is a complicated-subsystem (lesson 7) that shouldn't be mixed with the user-facing reviews capability—. Leadership's hurry for "a single team to launch fast" is about to create the same problem Mercado already has with the monolith: a big piece without internal boundaries, that will later hurt to split.
Part 2 — The executed measurement
# Project: Conway over a NEW Mercado capability (reviews + recommendations).
# We measure the friction before, apply the inverse maneuver, and measure again.
from itertools import combinations
# Modules of the new capability + the existing ones it consumes.
modules = ["reviews_api", "moderation", "rating_aggregate",
"recommendations", "ml_training",
"product_catalog", "auth", "notifications"]
deps = {
"reviews_api": ["moderation", "product_catalog", "auth", "notifications"],
"moderation": ["ml_training"],
"rating_aggregate":["reviews_api"],
"recommendations": ["rating_aggregate", "ml_training", "product_catalog"],
}
def total_friction(owners, service):
total = 0
for m in modules:
room = set(owners[m])
for d in deps.get(m, []):
if d not in service:
room |= owners[d]
total += len(list(combinations(room, 2)))
return total
def max_modules_per_team(owners):
counts = {}
for m in modules:
for t in owners[m]:
counts[t] = counts.get(t, 0) + 1
return max(counts.values()), counts
# BEFORE: a single squad 'growth' receives ALL the new capability (5 modules),
# mixing two different subsystems (user-facing reviews + ML).
before_owners = {
"reviews_api": {"growth"}, "moderation": {"growth"},
"rating_aggregate": {"growth"}, "recommendations": {"growth"},
"ml_training": {"growth"},
"product_catalog": {"catalog"}, "auth": {"platform"},
"notifications": {"platform"},
}
before_service = set()
# AFTER: inverse maneuver. A stream-aligned 'reviews' team (reviews_api,
# moderation, rating_aggregate) and a complicated-subsystem 'ml-reco'
# (recommendations, ml_training). catalog/auth/notifications as a service.
after_owners = {
"reviews_api": {"reviews"}, "moderation": {"reviews"},
"rating_aggregate": {"reviews"}, "recommendations": {"ml-reco"},
"ml_training": {"ml-reco"},
"product_catalog": {"catalog"}, "auth": {"platform"},
"notifications": {"platform"},
}
after_service = {"product_catalog", "auth", "notifications"}
fb = total_friction(before_owners, before_service)
fa = total_friction(after_owners, after_service)
mb, cb = max_modules_per_team(before_owners)
ma, ca = max_modules_per_team(after_owners)
print(f"{'scenario':<34}{'friction':>10}{'max modules/team':>20}")
print(f"{'BEFORE (1 growth squad)':<34}{fb:>10}{mb:>20}")
print(f"{'AFTER (inverse maneuver)':<34}{fa:>10}{ma:>20}")
print()
print(f"friction (coord_pairs) : {fb} -> {fa}")
print(f"load of the most loaded team: {mb} modules -> {ma} modules")
print(f"ownership before : {cb}")
print(f"ownership after : {ca}")
What to expect. Running it:
scenario friction max modules/team
BEFORE (1 growth squad) 4 5
AFTER (inverse maneuver) 2 3
friction (coord_pairs) : 4 -> 2
load of the most loaded team: 5 modules -> 3 modules
ownership before : {'growth': 5, 'catalog': 1, 'platform': 2}
ownership after : {'reviews': 3, 'ml-reco': 2, 'catalog': 1, 'platform': 2}
Read the numbers as you learned, and notice something subtle this case teaches and the checkout one doesn't: the coordination friction wasn't the worst problem here; the cognitive load was.
In the initial proposal, the friction is 4 —relatively low—, because since a single squad (growth) owns everything new, almost all the dependencies are internal to growth (they don't cross teams). A hurried engineer could look at that 4 and say "there's no coordination problem, let's leave it in one squad". That would be the mistake. The number that shouts is the other one: growth owns 5 modules, mixing two very different capabilities (user-facing reviews and ML). That squad is going to be overloaded —too much cognitive load, two domains that require different mindsets and expertise in the same head— and, worse, since there's no internal boundary between reviews and ML, the two subsystems are going to intertwine in the code (Conway: a team without internal boundaries produces a module without internal boundaries). It's lesson 3's monolith being born again, in miniature.
After the inverse maneuver, both numbers improve: the friction drops from 4 to 2 and the load of the most loaded team drops from 5 modules to 3. But the important change is qualitative, and it shows in the ownership: we went from {growth: 5} —a drowning squad— to {reviews: 3, ml-reco: 2} —two cohesive teams, each with one domain—. Now there's a real boundary between the reviews capability and the ML one, and by Conway, that organizational boundary is going to produce a clean boundary in the code: reviews and recommendations are going to be two separate subsystems, not one mixed ball.
The honest detail: the friction didn't drop to 0, it dropped to 2. That 2 is the irreducible coordination between reviews and ml-reco: moderation (reviews') depends on ml_training (ml-reco's), and recommendations (ml-reco's) depends on rating_aggregate (reviews'). That is, the two teams really need each other —reviews uses the moderation model trained by ml-reco, and ml-reco uses the ratings computed by reviews—. That's real business coordination, not artificial: it's not organized to eliminate, it's organized to cost the minimum (a stable contract between the two teams). The maneuver removed the artificial friction and load (a squad doing everything) and left the genuine coordination (two domains that feed each other). And along the way, product_catalog, auth, and notifications are consumed as platform services (that's why they came out of the co-change), not as things reviews has to coordinate on every change.
Part 3 — The inverse maneuver, with team topologies
The redesign, named with lesson 7's catalog:
-
reviewsteam — stream-aligned. Owns the reviews flow end to end:reviews_api,moderation,rating_aggregate. It's a user-facing capability with a clear journey (leave a review, see it moderated, see the rating). It consumes the platform (auth, notifications) and the catalog (product_catalog) as services (x-as-a-service), and consumes ml-reco's moderation model through a stable contract. -
ml-recoteam — complicated-subsystem. Ownsrecommendationsandml_training: the machine learning, which requires deep and scarce expertise (data scientists, ML engineers) that makes no sense to mix with the reviews team. It encapsulates that complexity so reviews (and others) consume recommendations and models without understanding their guts. It consumesrating_aggregatefrom reviews as input to train/recommend. -
catalogandplatform— unchanged, consumed as-a-service. product_catalog (catalog), auth and notifications (platform) stay where they were; the key is that the new capabilities consume them as stable services, not by getting their hands into them. That's why they don't appear in the coordination room of the new modules. -
Interaction between
reviewsandml-reco: x-as-a-service in both directions (with perhaps an initial touch of temporary collaboration at the start, while they define the contracts). reviews consumes ml-reco's moderation model; ml-reco consumes reviews' ratings. Each one exposes its own through a stable contract, so the irreducible coordination (minimum weight) flows through the cheapest channel.
What problem I solved: the mistake of the initial proposal wasn't the coordination friction (it was low, 4) but the cognitive load and the lack of a boundary —a single squad with five modules from two different domains, condemned to produce a mixed subsystem and to drown—. The inverse maneuver separated the two domains into two teams aligned to their capabilities (one stream-aligned reviews, one complicated-subsystem ML), lowered the load of the most loaded team from 5 to 3 modules, and created the organizational boundary that —by Conway— will produce a clean boundary in the code.
What coordination is irreducible: the remaining friction of 2 is real: reviews and ml-reco feed each other (moderation uses the ML model; recommendations use the ratings). That's not eliminated by reorganizing; it's made to flow cheaply through stable contracts. Trying to take it to 0 —for example, by duplicating the model training inside reviews so as not to depend on ml-reco— would be worse: it would create duplication and lose the concentrated expertise. The right organization recognizes the genuine dependency and lets it pass through the cheapest channel, doesn't abolish it.
When it would NOT be worth it: if Mercado were a reviews pilot with a single module and no ML yet (say, only reviews_api and rating_aggregate, without moderation or recommendations), putting it all in one growth squad would be the right thing —two modules of one domain, low load, zero need for boundaries—. The inverse maneuver is justified here because it's five modules from two different domains with different expertise; with two modules of one domain, splitting would be over-engineering. The module's rule: reorganizing is justified by a measured misalignment (or overload), not by reflex.
Exercises
These exercises transfer the method to other Mercado decisions, so you confirm you learned to design organizations and not to repeat one.
Exercise 1 — The missing enabling team. The reviews team, just formed, doesn't know how to do content moderation testing (proving the filter doesn't let offensive content through nor blocks legitimate content). Mercado has a team expert in testing ML systems. What team topology type is that expert team, what interaction mode should it use with reviews, and why must it have an expiration date?
See solution
The team expert in ML testing is an enabling team: its purpose is to help other teams acquire a new capability (moderation testing), not to own a module of its own. Its product is that the reviews team is left capable of doing that testing alone.
The correct interaction mode is facilitating: the enabling team accompanies reviews —teaches them the techniques, reviews the first tests with them, shares tools—, but doesn't do the work for them. The goal is to transfer the capability.
Why it must have an expiration date: because the facilitating mode is temporary by design. If the enabling team stayed permanently doing reviews's moderation testing, it would stop enabling and become a dependency —reviews would never learn, and the enabling would be a bottleneck for all the teams that need the same thing—. The enabling's success is measured by whether reviews was left autonomous, not by how much work it did. Setting an exit date (for example, "six weeks") forces the interaction to be a capability transfer and not a permanent substitution. It's the "enabling that becomes permanent" anti-pattern of lesson 7.
Exercise 2 — The dependency that contaminates. Suppose leadership insists on leaving recommendations and ml_training inside the reviews squad (not creating the ml-reco team), "so as not to have another team". With the method, argue with at least two numbers why it's a bad idea, and what concrete symptom you'd see in six months.
See solution
Number 1 — the cognitive load: leaving everything in reviews keeps a team with 5 modules from two different domains (user-facing reviews + ML), instead of two teams of 3 and 2 modules each. The load of the most loaded team stays at 5 instead of dropping to 3. A team that has to hold in its head both the reviews logic and the ML model training is overloaded —they're two different expertises—, and the overload translates into slowness and errors.
Number 2 — the absence of a boundary (Conway): with a single team owning reviews and ML, there's no organizational boundary between the two domains, so —by Conway's Law— the code won't have a boundary between them. The measured friction would be low (all internal to the team), but that's misleading: the low coordination friction hides a high internal coupling that no one sees until it hurts.
Symptom in six months: the reviews code and the recommendations/ML one will be intertwined with no boundary —the moderation module calling directly into the training guts, the recommendations logic mixed with the ratings one, data shared with no contract—. When eventually they want to separate the ML (to scale it separately, to hire a dedicated team, to reuse the models elsewhere), they'll find they can't without a painful refactor, because there never was a boundary. It's exactly lesson 3's monolith being born again: a big piece with no internal seams, perfect for the single squad that wrote it and a torment to split later. The lesson: creating the boundary now (when it's cheap, two teams from the start) is much cheaper than creating it later (when the code already mixed).
Exercise 3 — Another capability, same method. Mercado wants to add international (selling in other countries: currency handling, taxes per country, translations). Once again a single intl squad owning everything is proposed. Without running code, apply the method: what would you ask to decide whether a single squad suffices or whether it must be split? Give the criterion, not a fixed answer.
See solution
The method doesn't give a fixed answer —it gives a criterion evaluated with the right questions—. What I'd ask to decide:
-
How many modules and from how many different domains? If
internationalis a cohesive domain (currencies, taxes, translations are all "adaptation per country", thought about together), a single stream-aligned squad might suffice. If it hides two domains of very different expertise —say, a tax calculation engine that requires deep fiscal expertise, separate from the rest—, that engine could be a separate complicated-subsystem. -
What's the cognitive load? How many modules would the
intlsquad own? If it's two or three of one domain, the load is manageable (don't split). If it's six from mixed domains, it overloads (split). -
What dependencies cross toward other teams, and are they co-change or service? Does
internationalneed to get its hands into checkout, payments, the catalog (co-change, expensive), or does it consume them as stable services (cheap)? If it forces co-changing modules of other teams, cross-team friction appears that has to be designed (maybe stable contracts, maybe moving boundaries). -
Is there a natural boundary that, if I don't create it now, will be expensive to create later? The Conway criterion: if within
internationalthere are two subsystems that will eventually want to separate, it's cheaper to give them separate teams from the start than to let them mix and split them later.
The criterion, in one sentence: a single squad suffices if the capability is one cohesive domain with manageable cognitive load and few expensive cross-team dependencies; it must be split if it hides domains of different expertise, overloads a team, or lacks a boundary that will be expensive to create later. The answer depends on the numbers and the context —what you learned is to ask and measure, not to apply "always split" or "never split"—. That's treating the org chart as an architecture decision, which is the module's thesis.
Module summary and where you go next
With this project you close module 2, the social heart of the guide. You started with the thesis —you ship your org chart: the system's structure copies the organization's communication structure— and the two-crew bridge that shows the fragile seam falls where the organization is divided (lesson 1). You turned the law from phrase into tool, measuring Mercado's org↔code mirror —61.5% of cross-team dependencies, the communication graph the architecture demands— (lesson 2). You discovered the origin of the painful monolith: it's your first org chart, fossilized —perfect for the 2019 single team, broken for the 2024 five squads, without the code changing a line— (lesson 3). You understood the economic engine: coordination grows quadratically (n(n-1)/2), so a big team drowns and splitting into small ones lowers the coordination 5x with the same people (lesson 4). You diagnosed the concentrated friction: checkout, co-owned by two teams and touching four, contributes half of the system's friction (lesson 5). You applied the master cure: the inverse Conway maneuver —changing the organization to obtain the architecture—, which lowered the friction from 12 to 5 without touching the code (lesson 6). And you acquired the vocabulary to design organizations on purpose: the four team types and the three interaction modes of team topologies, with the cheap mode (x-as-a-service) lowering the load from 15 to 6 (lesson 7). Here, in the project, you did it all yourself on a new case: you mapped, measured the friction and the load, and redesigned with the inverse maneuver and the team topologies.
The capability you take away: facing any system, you can read its organization and predict its architecture, measure the misalignment between the two, diagnose where the friction concentrates, and redesign the organization —with the inverse maneuver and the team topologies catalog— to obtain the architecture the business needs. You stopped treating the org chart as a fixed fact to fight against, and started treating it as the first architecture decision.
Where you go next, within this guide:
- Module 3 — Communicating architecture. You already know how to design the org↔architecture restructuring; module 3 teaches you to communicate it so it's understood and adopted: the C4 model (Context/Container/Component/Code — the right diagram for each audience) and the ADR as the tool that makes a decision's why travel through time. The org↔architecture map and the inverse maneuver you designed here are worth nothing if you can't explain them to the VP who approves the reorganization and the team that lives it —and that's a craft in itself—.
And toward the rest of the ecosystem: every time this module talked about "independent services", "monolith", "microservices", or "stable contracts" without re-teaching them, it leaned on the guides that do teach them —architectural-styles-and-boundaries, api-design-and-integration, architecture-decisions-and-tradeoffs—. Now you have what none of them covers: the org↔architecture dynamic, the social force that decides whether those technical patterns can be sustained. Conway's Law is the reminder that architecture isn't made by diagrams: it's made by people, organized a certain way, communicating a certain way —and the architect's greatest leverage is right there—.
Resources
- Skelton & Pais — Team Topologies — the module's natural close: the whole book is the manual for designing organizations that produce good architectures, exactly what you did in this project. The four team types, the three modes, and the inverse maneuver, all in one place.
- Melvin Conway — "How Do Committees Invent?" (1968) — return to the origin to close the circle: Conway already anticipated in 1968 almost everything you measured, including the idea that reorganizing is the lever to change the system. Short and foundational.
- Martin Fowler — "Conway's Law" — the best summary of the whole module in two pages, with the inverse maneuver and the phrase that condenses it: when the system's architecture and the organization's clash, the organization's wins.