Module 3: Communicating Architecture

7. Avoiding the 500-page document and the spaghetti diagram

Overview

By the end of this lesson you'll recognize and avoid the two big failures of architecture communication, the ones that ruin even the work of someone who already masters C4 and the ADR. The first is the 500-page document no one reads: documenting too much and communicating too little, producing so much text that the signal drowns in the volume and the reader gives up before finding what they're looking for. The second is the spaghetti diagram: the sheet that shows the whole system —containers, components, classes, integrations, queues— all at once, with a hundred crossing arrows, technically complete and humanly illegible. The two failures share a root: they confuse completeness with communication. They believe that showing more is communicating more. It's the reverse: past a certain point, each extra element subtracts comprehension, because it covers the main idea. This lesson is the vaccine, and it brings two rules that can be measured: a diagram must not mix abstraction levels, and it must not exceed the limit of elements the human head can read.

This matters because these two failures are the default way architecture documentation goes wrong —not for lack of effort, but from misdirected excess—. The 500-page document was almost always written by a diligent person who wanted to be exhaustive; the spaghetti diagram was almost always drawn by someone who knew a lot about the system and wanted to show it all. Good intentions don't save the result: no one reads the document, no one understands the diagram, and the team wrongly concludes that "documenting is useless". It's useful —what's useless is documenting too much—. And there's a third failure, quieter, that closes the lesson: the diagram that rots because it lives in a wiki separate from the code, and describes precisely a system that no longer exists. A diagram that lies is worse than none.

Connection with the module: in lessons 2 to 5 you learned to produce good diagrams at the right level; in lesson 6, good ADRs. This lesson teaches the complementary skill: recognizing and avoiding bad communication, which is often produced with more work than the good kind. The rules you'll measure here —one diagram, one level; one diagram, few elements— are the concrete defense of everything before. And the principle that documentation must live versioned with the code (docs-as-code) is what keeps alive everything you produced. In lesson 8, the project, you'll run these validators over your own deliverables before deeming them good.

The map that draws every street, every cable, and every pipe

Return to maps, but imagine one made by someone obsessed with not omitting anything. On a single sheet they draw: the streets, yes, but also the metro lines, and on top the water pipes, and on top the electrical cables, and on top the bus routes, and the name of every business, and the terrain's contour lines, and the boundary of every district, all with the same line thickness, all on the same sheet. Is it complete? Absolutely —all the city's information is there—. Is it useful for anything? Not at all. No one can find a street in that tangle, because the street they're looking for is buried under ten layers of something else. Completeness made it useless. A map communicates because it omits: the metro map is useful precisely because it does not draw the pipes.

Now imagine the text version: the 500-page instruction manual that comes with an appliance and documents every screw, every safety regulation of every country, every mode the device will never use. It's exhaustive. And that's why no one reads it: when you want to know how to turn the device on, the instruction is on page 237, between the warning about high-altitude use and the parts table for the 1998 model. The manual you do read is the "quick start" card: one page, five steps, what 95% of people need. It's not less honest —it's more useful, because it communicates instead of archiving—.

The spaghetti diagram is the map of all the layers; the 500-page document is the complete manual. Both sin in the same way: they believe their job is to contain everything, when their job is to communicate something to someone. And communicating requires omitting: choosing the level (one map, one layer), choosing the essential (one page, five steps). The architect who learns this stops measuring their documentation by how complete it is and starts measuring it by what people understood and used. Let's make the two rules that avoid it measurable.

The two hygiene rules, executed

The two diagram failures can be detected automatically, because they have concrete signatures. The spaghetti has too many elements. The confusing diagram mixes abstraction levels that shouldn't coexist. The following code validates two Mercado diagrams against those two rules: one well made (the Container you drew in lesson 3) and one that "shows everything" (containers + components + classes on a single sheet). For each it reports whether it mixes levels, whether it's spaghetti, and the verdict.

# Two diagram hygiene rules, really executed.
# Rule 1: a diagram must not MIX abstraction levels.
# Rule 2: a diagram must not exceed the legible limit (spaghetti diagram).

# Which abstraction "rank" each C4 element type belongs to.
# person and external_system are CONTEXT: allowed as decoration at any level.
# The "internal" types are the ones that define the diagram's level.
CORE_RANK = {
    "container": 2,
    "component": 3,
    "class": 4,
    "method": 4,
}
CONTEXT_TYPES = {"person", "external_system", "software_system"}

# Legibility threshold: beyond this, the diagram stops communicating.
LEGIBLE_LIMIT = 20  # elements per diagram (C4 rule of thumb)

RANK_NAME = {2: "Container", 3: "Component", 4: "Code"}


def check_mixing(elements):
    core_ranks = sorted({CORE_RANK[e["type"]] for e in elements if e["type"] in CORE_RANK})
    levels = [RANK_NAME[r] for r in core_ranks]
    mixed = len(core_ranks) > 1
    return mixed, levels


def check_spaghetti(elements, relationships):
    n = len(elements)
    r = len(relationships)
    too_big = n > LEGIBLE_LIMIT
    return too_big, n, r


def audit(name, elements, relationships):
    print(f"== {name} ==")
    mixed, levels = check_mixing(elements)
    too_big, n, r = check_spaghetti(elements, relationships)
    if mixed:
        print(f"  MIXES LEVELS:  yes -> combines {' + '.join(levels)} in a single diagram")
    else:
        level = levels[0] if levels else "context only"
        print(f"  MIXES LEVELS:  no  -> a single internal level ({level})")
    if too_big:
        print(f"  SPAGHETTI:     yes -> {n} elements > limit {LEGIBLE_LIMIT}; {r} arrows: illegible")
    else:
        print(f"  SPAGHETTI:     no  -> {n} elements <= limit {LEGIBLE_LIMIT}; {r} arrows: legible")
    verdict = "REJECTED" if (mixed or too_big) else "OK, communicates"
    print(f"  VERDICT:       {verdict}")
    print()


# Diagram 1: Mercado's Container, well made.
container_good_elems = [
    {"type": "person", "name": "Customer"},
    {"type": "person", "name": "Seller"},
    {"type": "container", "name": "Web App"},
    {"type": "container", "name": "Mobile App"},
    {"type": "container", "name": "API"},
    {"type": "container", "name": "Database"},
    {"type": "container", "name": "Search Index"},
    {"type": "external_system", "name": "Payment Gateway"},
    {"type": "external_system", "name": "Carrier API"},
]
container_good_rels = [
    ("Customer", "Web App"), ("Customer", "Mobile App"), ("Seller", "Web App"),
    ("Web App", "API"), ("Mobile App", "API"),
    ("API", "Database"), ("API", "Search Index"),
    ("API", "Payment Gateway"), ("API", "Carrier API"),
]

# Diagram 2: the "shows EVERYTHING" diagram - containers + components + classes together.
everything_bad_elems = (
    container_good_elems
    + [{"type": "component", "name": f"Comp{i}"} for i in range(1, 9)]
    + [{"type": "class", "name": f"Class{i}"} for i in range(1, 8)]
)
everything_bad_rels = [(f"n{i}", f"n{i+1}") for i in range(1, 35)]

audit("Mercado Container (well made)", container_good_elems, container_good_rels)
audit("'Show everything' diagram (a single sheet)", everything_bad_elems, everything_bad_rels)

What to expect. Running it:

== Mercado Container (well made) ==
  MIXES LEVELS:  no  -> a single internal level (Container)
  SPAGHETTI:     no  -> 9 elements <= limit 20; 9 arrows: legible
  VERDICT:       OK, communicates

== 'Show everything' diagram (a single sheet) ==
  MIXES LEVELS:  yes -> combines Container + Component + Code in a single diagram
  SPAGHETTI:     yes -> 24 elements > limit 20; 34 arrows: illegible
  VERDICT:       REJECTED

The two verdicts tell the story. The well-made Container passes both rules: a single internal level (everything is containers), 9 elements under the limit of 20. It communicates. The "shows everything" diagram fails both at once —and not by chance, because the two failures tend to come together—: it mixes three levels (containers, components, and classes on the same sheet, which is the city map with the pipes and cables stacked on top) and it's spaghetti (24 elements, 34 arrows, well above what a human eye follows). The validator has no aesthetic taste; it just counts. And just by counting, it distinguishes the diagram that communicates from the one that doesn't.

Notice the mixing rule, because it's subtle. The validator does not flag the people or the external systems as "mixing" —those are context, decoration allowed at any level (a Container legitimately shows the customers around)—. What it flags is mixing the internal elements of different levels: containers with components with classes. That's the real signature of the confusing spaghetti: not "it has many boxes", but "it has boxes of incompatible zoom levels on the same sheet". It's exactly the map that draws the streets and the pipes: each layer separately would be useful; stacked, neither is.

How the two diagrams look, side by side

The validator counts; the eye confirms. Here's how the well-made Container looks —legible, one level, few boxes—:

   Customer ──▶ Web App ──▶ ┌─────┐ ──▶ Database
   Seller   ──▶ Mobile ───▶ │ API │ ──▶ Search Index
                            └─────┘ ──▶ Payment Gateway
                                    ──▶ Carrier API

   9 boxes, one level (containers). You follow each arrow with your finger.

And here's how the spaghetti diagram looks, schematized —all levels stacked, impossible to follow an arrow—:

  Customer─┐  ┌Web─┬─API──Comp1─Class1   Search─Comp5
     Seller┼─▶│    │   │╲   │  ╳  │   ╲    │  ╳   │
  Mobile───┘  └Comp2╲ Class2─Comp3 Class3─Comp6─Class4
     │  ╳  │   │  ╳ ╲│  ╳ │ ╲ │ ╳ │  ╳  │ ╲ │  ╳
  Payment─Comp4─Class5─DB─Comp7─Class6─Carrier─Comp8─Class7
     └──── 24 boxes, 3 levels, 34 arrows: nobody reads this ────┘

You don't need to understand the second diagram —that's the point—. Your eye bounces, can't find where to start, and gives up. The same information that in the first is clear (Customer uses the web, the web calls the API) is here present but illegible, buried under components and classes that belong to other zoom levels. Complete, yes. Communicative, no. The whole skill of this lesson is preferring the first to the second, even though the second "has more information" —because communicating isn't containing information, it's transmitting it to a human head—.

The third failure: the diagram that rots

There's a way to fail that no content validator catches, because it's not about the diagram itself but about where it lives. A perfect diagram —well leveled, legible— becomes harmful if it describes a system that already changed. The diagram that in 2024 showed Mercado's five containers precisely, today lies if checkout was already extracted to a sixth and no one updated the drawing. And it lies worse than a bad diagram, because it looks true: the new dev believes it, acts on it, and crashes into reality. An outdated diagram has authority without truthfulness —the worst combination—.

The cause is almost always the same: the diagram lives separate from the code, in a wiki, in a presentations folder, in someone's cloud. When the code changes, the code changes; the diagram, on its island, doesn't find out. No one has the reflex to update a file that lives in another system, in another workflow. Documentation in a separate wiki rots by design, not by carelessness.

The cure is called docs-as-code: the documentation —the diagrams and the ADRs— lives in the same repository as the code, versioned alongside it, and changes in the same commit and the same review as the code it describes. This has three effects that change everything. First, the diagram is updated when the code is updated, because they're in the same change and the reviewer sees it. Second, the diagram has history: you can see how the architecture evolved commit by commit, just like the code. Third —and that's why the previous lessons insisted on small diagrams and ADRs that fit on one screen, and on generating the Code instead of drawing it—: the diffable text artifacts (mermaid, an ADR in Markdown, a diagram as code) live well in a repository; a 500-page binary document or a hand-exported image, not. The whole module pushed toward small and textual artifacts precisely so they can live versioned with the code and not rot. Documentation that survives is the one that shares the code's fate; the one that lives apart, dies apart. (The ecosystem's documentation guide goes deeper into docs-as-code; here the principle is enough: version the docs with the code, or it rots.)

Common mistakes

Measuring documentation by its weight (of exhaustiveness). What happens: the team takes pride in a 500-page architecture document or a diagram that "has the whole system", and confuses that volume with quality. No one reads it or understands it. Why it happens: completeness is visible and measurable ("look how much we documented") while effective communication is invisible ("did anyone understand?"). How to spot it: if you describe your documentation by its size ("500 pages", "a huge diagram with everything") instead of by its effect ("onboarding dropped to two days"), you're measuring the wrong thing. How to fix it: measure by use and comprehension —did anyone consult it to decide something this week? did the new dev orient themselves with this?—; and remember that communicating requires omitting, so a shorter artifact usually communicates more.

Mixing levels "so the relationship shows" (of connection). What happens: the architect wants to show how a specific class connects with an external system, and to do so they put the class (Code level) and the external system (Context level) in the same diagram along with containers and components —and create the spaghetti—. Why it happens: the intention is good (show a real relationship that crosses levels), but the execution mixes incompatible zooms. How to spot it: this lesson's validator catches it —if your diagram has internal elements of more than one rank (container + component + class), you mixed—. How to fix it: if you need to show a relationship that crosses levels, do it in the diagram of the higher of the two, representing the other end as a box of that level; don't bring classes down to the containers diagram. One diagram, one level.

Putting the diagrams in a separate wiki (of convenience). What happens: the diagrams and ADRs live in Confluence, Notion, or a Drive folder, separate from the code. At first it's convenient (nice drawing tools); six months later, everything is out of date and lies. Why it happens: wiki tools are nicer for drawing than a text file in the repo, and today's convenience wins over tomorrow's maintenance. How to spot it: if your diagram lives in a system where the code can't "see" it and doesn't change in the same commit as the code, it's going to rot. How to fix it: docs-as-code —diagrams as text (mermaid, diagram-as-code) and ADRs in Markdown, in the same repository, reviewed in the same pull request as the code they describe—.

Exercises

Exercise 1 — Diagnose the diagram. A Mercado diagram contains: 3 people, the Mercado system box, 5 containers, 6 API components, 4 checkout classes, 2 external systems, and about 40 arrows. Run it through the two lesson rules: does it mix levels? is it spaghetti? what's the verdict and how do you fix it?

See solution

Does it mix levels? Yes. It has internal elements of three different ranks: containers (rank 2), components (rank 3), and classes (rank 4). The people and the external systems don't count as mixing (they're context/decoration), but containers + components + classes on a single sheet are three zoom levels stacked. It's the map with streets, metro, and pipes together.

Is it spaghetti? Yes. Count the elements: 3 people + 1 system + 5 containers + 6 components + 4 classes + 2 externals = 21 elements, plus 40 arrows. It passes the legible limit of 20, and 40 arrows are impossible to follow with the eye.

Verdict: REJECTED by both rules at once (the typical pattern: the two failures go together).

How you fix it: by splitting it into the set of C4 diagrams, one per level. (1) A Context: the 3 people, Mercado as a box, the 2 externals —5-6 elements, legible—. (2) A Container: the 5 deployable pieces with people and externals as decoration —9 elements, legible—. (3) A Component only of the container that warrants it (the API/checkout): its 6 components —legible—. The 4 checkout classes don't go in any hand-drawn diagram: they're read from the code or generated (Code level). Result: 2-3 clean diagrams that together contain all the information of the original monster, but each one communicates. Completeness lives in the set, not in the sheet.

Exercise 2 — The document no one reads. Mercado's architect wrote a 180-page architecture document: it includes the history of every decision, diagrams of the four levels of every container, endpoint listings, table schemas, and a glossary. Six months later, the new devs' onboarding still takes two weeks and no one cites the document. What went wrong and what should they have produced instead?

See solution

What went wrong was the confusion of completeness with communication: the architect produced an exhaustive artifact thinking "more complete = better documented", but 180 pages are useless for real use —when the new dev needs to orient themselves, they're not going to read 180 pages; they give up and ask people, and that's why onboarding is still two weeks—. The document is an archive, not a communication: it exists, gives the feeling of being documented, and no one uses it. Besides, a document that big rots: keeping 180 pages up to date is impossible, so six months later a good part already lies.

What they should have produced: little and alive, aimed at use. For the onboarding —the concrete problem that wasn't solved— a short README in the repo with the Context and the Container (the two diagrams that orient) and three paragraphs of "how you run the system locally and where to start". For the why of the important decisions, a handful of short ADRs in the repo, not the "history of every decision" in prose. For the detail (endpoints, schemas), link to what's generated from the code and is always up to date, not copy it by hand into the document. The result: instead of 180 dead pages, a README + Context + Container + a few ADRs, all in the repo, versioned with the code, that a new dev actually reads and uses to get going in two days. Less, alive, used —beats more, dead, ignored—.

Exercise 3 — Why did it rot? Two teams document equally well (good C4 diagrams, good ADRs), but a year later team A has up-to-date documentation and team B has documentation that lies. The only difference: A keeps its diagrams as mermaid files and its ADRs as Markdown in the code repository; B keeps them in a very nice separate corporate wiki. Explain why that single difference produced opposite results, and what principle captures it.

See solution

The decisive difference is whether the documentation shares the code's workflow. In team A, a diagram is a text file in the same repo: when someone changes the code in a pull request, the affected diagram is right there, the author updates it in the same change, and the reviewer sees it and demands it. Updating the docs isn't a separate step that gets forgotten; it's part of the same commit. The docs stay alive because they can't go out of sync without someone noticing in the review.

In team B, the diagram lives in a wiki, in another system, in another flow. When someone changes the code, nothing in their workflow reminds them that a diagram exists in the wiki; updating it is a voluntary, separate act that competes with the day's rush. Most of the time it doesn't happen. The docs rot because they're structurally disconnected from the code they describe —not by the people's carelessness, but by where they live—.

The principle that captures it is docs-as-code: the documentation is versioned with the code, in the same repository, and changes in the same review. It's not that wiki tools are bad —it's that separating the docs from the code guarantees they go out of sync—. That's why this whole module pushed toward small and textual artifacts (mermaid, ADRs in Markdown, generating the Code): not out of minimalist taste, but because they're the ones that can live in the repo and share the code's fate. Documentation that survives is the one that travels with what it describes.

Summary and next step

In this lesson you learned to recognize and avoid the three ways architecture communication goes wrong, almost always from misdirected excess. The 500-page document (documenting too much, communicating too little) and the spaghetti diagram (showing everything, communicating nothing) share the root of confusing completeness with communication —and you saw, with the map that draws all the layers, that communicating requires omitting—. You made the two rules measurable: a diagram must not mix abstraction levels (containers with components with classes) nor exceed the legible limit —the validator rejected the "shows everything" with 24 elements and three levels, and approved the clean Container with 9 and one—. And you saw the third failure, the diagram that rots from living apart from the code, and its cure: docs-as-code, documentation versioned with the code so it shares its fate.

Before moving on you should be able to: diagnose a diagram with the two rules (mixing of levels, count of elements) and split it into the clean C4 set; explain why a giant document or a total diagram communicate less, not more; and justify docs-as-code as the defense against the diagram that lies.

What follows is putting it all together. In lesson 8, the project, you'll act as Mercado's architect and produce its communication package for two audiences: the Context for the VP, the Container for the new dev, an ADR that communicates the why of a decision, and the executed validation that each map aims at the right level for its audience and passes the hygiene rules you just learned. It's the step from "I know each tool separately" to "I know how to assemble the complete package that communicates an architecture to those who need it".

Resources