When a Container Earns Its Own Zoom

July 07, 2026 · 14 min read

A week after the substitution engine shipped, the team realises the C2 container diagram doesn’t tell the truth about what’s inside the Supply Matching container. The question isn’t whether to draw a component diagram. It’s whether to draw one at all.

Ravi has been at Greenbox for four days. He joined as a new Go developer to help carry the load that Tom and Priya were never going to be able to hold alone past the Melbourne launch. He’s methodical, reads more than he talks, and he has a notebook, a real one, with a pen, that he writes in during every meeting.

On his fourth morning he walks up to Priya’s desk and puts the notebook down in front of her. It’s open to a page that has “Supply Matching” at the top and an increasingly frustrated-looking set of arrows underneath.

“Can you walk me through this container?” he says. “I’ve been reading the code since Monday. I think I understand the substitution engine. I think I understand the decision tables. I think I understand how farm availability gets in. But when I put all of those together in my head, the shape keeps slipping.”

Priya looks at the notebook. She looks at the screen, where Ravi has about six files open in VS Code.

“Yeah,” she says. “The diagram’s lying to you.”

“Which diagram?”

“The C2.” She pulls up the architecture diagrams page Tom set up three weeks ago, after Charlotte’s session. She points at the Supply Matching box in the container view. “That box. It’s one box on the diagram. But inside that box there are at least five things doing different jobs, and three of them talk to each other in ways you’d never guess from the code without reading for a week.”

Ravi nods slowly. “I was starting to think that.”

“You’re not wrong. I’ve been thinking about it too. Let me get Charlotte.”

The wrong kind of relief

Charlotte is in Sydney this week. Priya catches her on a video call after standup. She explains the problem: the C2 diagram shows Supply Matching as a single container, but Supply Matching is not a single container’s worth of complexity any more. It used to be. Six weeks ago, when they first drew the C2, Supply Matching was “take farm availability, match it to subscriptions, hand the result to Fulfilment.” That’s four hundred lines of Go. You don’t need a diagram for four hundred lines of Go.

Then the decision tables happened. And the substitution engine happened. And the CSV loader happened. And the completeness test grew into a whole test suite. And now Supply Matching is a thousand lines of Go with five identifiable moving parts, and Ravi can’t load it into his head in three days because nothing on the diagrams tells him what the shape is.

Charlotte’s first reaction is to be pleased. Her second reaction is to be careful.

“I’m pleased because this is exactly the moment I was waiting for. You’ve got a container that genuinely deserves a zoom. Most teams draw component diagrams for every container on day one, and ninety percent of them are useless because the container is ‘REST API plus database’ and nobody needed a picture.”

“Okay.”

“I’m careful because now you’re going to want to draw C3 for everything else too, and you should not. So before we draw this one, I want to talk about when you don’t draw a C3. Because the rule is actually more important than the technique.”

Priya gets her notebook out. Charlotte is about to say something Priya wants to write down.

The C3 anti-rule

Charlotte says it flatly, the way she says things when she wants them to be quoted back to her later.

“C3 is the level you don’t draw until you need it. And ‘need it’ has a specific meaning. It’s not ‘it would be nice to understand.’ It’s not ‘new joiners get confused sometimes.’ It’s: you cannot explain this container without drawing it, and you have had to explain it more than twice, and the explanation you gave the first two times was slightly different each time.

Priya writes that down.

“The reason C3 goes wrong,” Charlotte continues, “is that it’s tempting. It’s the level where the diagram starts to feel like it’s describing real things, modules, packages, classes. Developers love it because it’s close to the code. That’s the trap. If you draw C3 for every container, you end up with a pile of diagrams that are really just a prose description of the package layout, and those diagrams go stale in the time it takes to do one refactor. You’ll hate them within a month. You’ll stop updating them. And then when there’s a container that actually needs a C3, nobody will trust any of them because the others are lies.”

“So how do we know which ones need it?”

“Ask three questions.”

She puts them up:

Question What a "yes" means
Does this container have more than one kind of responsibility inside it? Not several CRUD endpoints — several different jobs that happen to live together.
Do the internal parts talk to each other in non-obvious ways? A cache, a queue, a rules engine, a scheduler — things that aren't "handler calls repository".
Has someone had to explain this container more than twice, differently each time? The team's shared mental model hasn't converged. A picture would converge it.

“If all three answers are yes, draw it. If only one or two are, don’t bother. Write a paragraph in the README. A paragraph beats a diagram that isn’t needed, every time.”

Priya runs Supply Matching through the questions.

  1. More than one kind of responsibility? Yes. It matches farm supply to subscriptions, it decides substitutions from decision tables, it caches availability, it audits decisions for review, and it lets Maya and Anika edit rules through a tiny internal admin tool.

  2. Non-obvious internal communication? Yes. The availability cache is refreshed by a background worker and read by the decision table interpreter on every subscription match. The audit logger writes to a separate table that the admin tool reads from. None of that is visible from the API surface.

  3. Explained more than twice, differently each time? Yes. Priya explained it to Anika one way, to Ravi another way, and to Tom a third way. She caught herself doing it on Monday.

“All three,” she says.

“Draw it.”

What’s inside the box

Priya gets Ravi to help. It’s his question that prompted it, and he’s about to understand the container better than anyone except Priya. That’s fine, that’s the whole point of documentation: the person who writes it learns more than the person who reads it.

They sit at a table with two laptops and a whiteboard and the existing C2 diagram on the wall behind them. Ravi draws on the whiteboard. Priya edits the Structurizr DSL on her laptop. The conventions from Charlotte’s session are still fresh: text is the source of truth, the whiteboard is the draft.

“Let’s list the things in here first,” Ravi says. “Not diagrams yet. Just lists.”

He writes on the whiteboard:

  • Farm Availability API. REST endpoints that farm partners post to, once a week
  • Availability Cache, in-memory snapshot of current-week availability, refreshed every 30 seconds
  • Decision Table Interpreter, the thing that reads the CSV tables and evaluates rules
  • Substitution Engine, the service that answers “what do I put in this box given these conditions”
  • Subscription Matcher, the thing that walks all subscriptions and asks the engine for each one
  • Audit Logger, records every substitution decision with its reasoning
  • Rule Editor (admin tool), a small web UI for Maya and Anika to upload new CSVs

Seven things. Ravi looks at it.

“That’s a lot for one container.”

“Yes.”

“Should some of these be their own containers?”

Priya considers it honestly. That’s a good question. It’s the kind of question C3 diagrams are supposed to provoke. A C3 diagram that doesn’t make you ask “should this be a separate container?” at least once isn’t earning its keep.

“Maybe,” she says. “Probably not yet. The audit logger and the rule editor could be split out later. But right now they share a database, they’re deployed together, they’re developed by the same people, and the volume isn’t high enough that we get any operational benefit from splitting. The point of C3 is to see the shape clearly, not to justify splitting.”

Ravi writes that down in his notebook.

Drawing it

Priya writes the DSL. The container section for Supply Matching grows from a single line to a nested block.

supplyMatching = container "Supply Matching" "Farm availability, preferences, substitutions" "Go, PostgreSQL" {
    availabilityAPI = component "Farm Availability API" "Accepts weekly availability submissions" "Go HTTP handler"
    availabilityCache = component "Availability Cache" "In-memory snapshot of current-week supply" "Go, sync.Map"
    tableInterpreter = component "Decision Table Interpreter" "Loads and evaluates substitution CSVs" "Go"
    substitutionEngine = component "Substitution Engine" "Answers substitution questions given constraints" "Go"
    matcher = component "Subscription Matcher" "Walks subscriptions, requests substitutions per box" "Go, scheduled job"
    auditLogger = component "Audit Logger" "Records every substitution decision with its reasoning" "Go, PostgreSQL"
    ruleEditor = component "Rule Editor" "Internal admin tool for uploading rule CSVs" "Go, htmx"

    availabilityAPI -> availabilityCache "writes"
    availabilityCache -> substitutionEngine "reads"
    tableInterpreter -> substitutionEngine "provides rules"
    ruleEditor -> tableInterpreter "uploads new CSVs"
    matcher -> substitutionEngine "asks per subscription"
    substitutionEngine -> auditLogger "writes decision + reasoning"
}

Then, in the views section, a new view:

component supplyMatching "SupplyMatchingComponents" {
    include *
    autolayout tb
}

She pushes. The GitHub Action renders the new view. The result is seven boxes in a readable layout with labelled arrows between them.

Ravi looks at the rendered diagram and does what Ravi does: he opens his notebook to a clean page and copies the shape onto paper, renaming nothing, just drawing it by hand. It’s the thing he does when he wants to remember something.

“Okay,” he says. “Now I can hold it.”

“Say it back to me.”

“Farm partners hit the availability API once a week. The availability cache keeps their numbers hot. When it’s time to match, the subscription matcher walks every subscription and asks the substitution engine what to put in each box. The engine looks up rules via the decision table interpreter, checks the cache for what’s actually available, and returns a decision. Every decision goes to the audit logger so Sam can check it later. Maya and Anika edit the rules through the rule editor, which reloads the interpreter.”

“Right.”

“I couldn’t have said that on Monday.”

“No. And that’s why the diagram was worth drawing. If you could have said it on Monday without the diagram, we wouldn’t have needed one.”

Ravi writes that down too.

Showing Tom

Tom comes over at lunchtime and looks at the new view. He raises his eyebrows.

“You drew a C3.”

“We drew one C3.”

“Is this the start of drawing them for every container?”

“No. Charlotte was very clear. It’s this one, because this one is genuinely complicated. The Subscription container is not getting one. Billing is not getting one. Fulfilment is not getting one. The rest are handler calls repository. There’s nothing to show.”

Tom nods. He’s relieved. Tom has the same allergy to unnecessary diagrams that Charlotte warned about, and he was half-expecting a week of Priya-driven documentation that he’d quietly hate.

“Good,” he says. “So what’s the rule?”

Priya reads it off her notebook, word for word, because she wants Tom to have the same version she wrote down.

“C3 is the level you don’t draw until you need it. Draw it when a container has more than one kind of responsibility, its parts talk to each other in non-obvious ways, and someone’s had to explain it more than twice differently each time. Otherwise write a paragraph in the README.”

Tom takes a photograph of the notebook page with his phone. “I’m putting that in the architecture README.”

“Good idea.”

He adds one line of his own underneath it: “If in doubt, don’t.”

The thing Priya didn’t know she needed

Late afternoon. Priya is finishing up. Ravi is in the quiet corner of the office reading the decision table interpreter code with the diagram open on his other monitor. Every few minutes he nods to himself in a small private way.

Priya notices something she wasn’t expecting. Now that the C3 diagram exists, she has found three things in the code that she wants to move around.

The audit logger is currently called from inside the substitution engine. But looking at the diagram, she can see that the audit logger is a crosscutting concern, it shouldn’t be inside the engine, it should be called by the matcher after the engine returns. That way the engine is pure: it takes inputs and returns decisions, and the fact of logging happens at a different layer. It’s more testable. It’s easier to swap out if they want to log to a different place later.

The availability cache has a direct dependency on the PostgreSQL connection. But the diagram shows it should be fed by the availability API, not by the database. The current code takes a shortcut that the diagram reveals. It’s a small shortcut, but a shortcut.

The rule editor talks directly to the decision table interpreter’s in-memory state, which is a thing Priya didn’t realise was happening until she wrote the arrow for it. It’s working, but it’s a hack. A cleaner version would have the rule editor publish a “rules updated” message and the interpreter subscribe to it.

Three refactors, all provoked by drawing the picture.

She Slacks Charlotte: “Weird thing. Drawing the C3 made me realise three things I want to change about the code. I didn’t set out looking for them. The diagram just made them obvious.”

Charlotte replies two minutes later: “That’s normal. It’s one of the reasons to draw C3 for the containers that earn it. You can’t see bad shapes until you draw the shape.”

Priya reads this and has a small moment of I wish I’d done this a week ago. But then she remembers Charlotte’s other line: you can’t draw C3 for everything. If she’d drawn it for Subscription or Billing a week ago, she wouldn’t have got a revelation; she’d have got a picture of four boxes labelled “API / service / repo / database” and thirty minutes of her life back. The value is in the discernment, not the drawing.

What Tom puts in the README

Tom writes a paragraph at the top of docs/architecture/components.md. It’s two sentences long.

We don’t draw component diagrams by default. We draw them only for containers that have more than one kind of responsibility, non-obvious internal communication, and a track record of being explained differently each time someone asks. The current list: Supply Matching. That’s it.

He commits. He Slacks the team channel: “New rule about C3 diagrams. If you want to add one, check the list first and talk to me.”

Kai replies with a thumbs up. Anika replies with “I understand maybe three words of that but I like the energy.” Tom sends her a link to the component diagram for Supply Matching. She replies ten minutes later: “Okay. Now I understand more words.”

The small meeting about the three refactors

Before Priya goes home, she blocks twenty minutes with Ravi for tomorrow morning. The title on the calendar invite is “Three things the diagram showed us.”

They’ll do them together. It’ll be Ravi’s first meaningful code contribution, because the thing Charlotte keeps saying, the diagram teaches you the thing, is about to become true for him by actually working on the code the diagram describes.

On her way out of the office, Priya walks past the architecture diagrams print that Tom tacked up near the coffee machine. The C1. The C2. And now, pinned next to them, a new printed page: the C3 for Supply Matching. Seven boxes. Labelled arrows. A small caption: “Drawn 10 June. If you’re reading this because you’re onboarding, this is the container that’s worth the zoom. The others aren’t.”

Charlotte’s rule, pinned to the wall.

Priya smiles at it and goes home.

For the workshop pattern behind this kind of session, running a C4 modelling workshop from scratch, including when to skip C3 entirely, see The Workshop: C4 Modelling. For the story of how the first C1 and C2 came about, see Drawing the System: From Event Storm to C4.

These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.