Skip to content

Diagramming & the C4 Model

  • Primary references:
    • The C4 model by Simon Brown — free, the lingua franca for software architecture diagrams
    • D2 — a modern text-to-diagram language (the source for this track’s C4 diagrams)
    • Mermaid — diagrams-as-code that renders natively in GitHub Markdown
  • Supplementary: diagrams.net/draw.io (free, when you need freehand), PlantUML, Structurizr (C4-native, by the model’s author)
  • Prerequisites: none beyond having a system worth drawing
  • Estimated time: 1 week at 4-6 hrs/week
  • A diagram is an answer to a question. Pick the diagram type by the question you’re answering, not by the tool you happen to have open.
  • Diagrams-as-code beats drag-and-drop for anything that must survive: it diffs, reviews, versions, and renders in CI like source.
  • The C4 model gives you a zoom level for architecture: Context → Container → Component → Code, so you stop drawing one diagram that tries to say everything and therefore says nothing.
  • An embedded, rendered-in-repo model stays alive; a screenshot in a wiki is dead the moment it’s pasted.
  • Install d2 (brew install d2 or see d2lang.com). Render this track’s diagrams: cd ../diagrams && d2 c4-container.d2 out.svg.
  • For each level of C4, redraw Streamflow from scratch without looking. If you can’t, you don’t understand the system yet — that’s the point.
  • Make one change to a .d2 file (add the DLQ consumer, say) and git diff it. Notice you reviewed an architecture change as a code change.

Every diagram should answer one explicit question for one audience. The two failure modes are (1) the “everything diagram” that mixes a CEO’s mental model with a packet’s TCP flags, and (2) the screenshot that can never be updated. The C4 model fixes the first by giving you abstraction levels (zoom in/out like a map); diagrams-as-code fixes the second by making the diagram a source artifact — text that lives next to the code, renders deterministically, and is reviewed in pull requests.

The map analogy (Simon Brown): Google Maps lets you zoom from country → city → street → building. You’d never show a single map at all zoom levels at once. C4 is that zoom control for software.

LevelNameAudienceAnswers
1Contexteveryone, incl. non-technicalWhat is this system, who uses it, what does it depend on?
2Containertechnical staffWhat are the separately deployable/runnable units, and how do they communicate?
3Componentdevelopers of one containerWhat are the major code structures inside a container?
4Code(rarely drawn by hand)Class/ER detail — generate this from code, don’t maintain it

The discipline: draw 1 and 2 for almost everything; draw 3 only for containers with non-obvious internals; almost never hand-draw 4. A “container” in C4 has nothing to do with Docker — it’s any independently runnable thing (an app, a service, a database, a Kafka topic, a single-page app).

The whole system is one box; everything else is users and external systems.

Streamflow system context (C4 Level 1)

Rendered from diagrams/c4-context.d2. Mermaid equivalent (renders inline on GitHub):

graph TD
    customer([Customer<br/>Person])
    ops([Platform Operator<br/>Person])
    sf[Streamflow<br/>Software System]
    pay[Payment Gateway<br/>External]
    notif[Notification Provider<br/>External]
    customer -->|Places orders, checks status| sf
    ops -->|Configures, observes| sf
    sf -->|Authorizes & captures| pay
    sf -->|Sends confirmations| notif
    notif -->|Email / SMS| customer

Zoom into the system box. This is the most useful diagram in practice — it maps 1:1 onto what you deploy (Containers, Kubernetes & Workloads) and how data flows (Distributed Workers).

Streamflow container view (C4 Level 2)

Source: diagrams/c4-container.d2.

Zoom into one container (here, the Order Worker) to show how a single message flows through its internals — the consume loop, idempotency guard, dispatcher, handlers, repository, producer.

Streamflow worker component view (C4 Level 3)

Source: diagrams/c4-component.d2.

C4 also defines a deployment diagram that maps logical containers onto infrastructure nodes. This is the bridge into the Infrastructure track — it shows the same containers living as Kubernetes objects, so it is co-located with the workload it depicts:

Streamflow deployment on Kubernetes

Source: infrastructure/01-containers-kubernetes/diagrams/deployment-k8s.d2.

2. Diagrams-as-code: D2 vs Mermaid vs the rest

Section titled “2. Diagrams-as-code: D2 vs Mermaid vs the rest”
ToolRenders in GitHub MD?StrengthUse when
Mermaid✅ nativelyZero toolchain, lives in the READMEInline diagrams reviewers see without rendering; sequence/flow/ER/state
D2❌ (commit SVG)Beautiful layout, real layout engines (ELK/dagre), themes, classesCanonical architecture diagrams you render to SVG and embed
PlantUML❌ (commit SVG)Mature C4 macro library, UML coverageTeams already standardized on UML/PlantUML
Structurizr❌ (own viewer)C4-native; one model, many auto-derived viewsYou want a single model and generated Context/Container/Component views
draw.io❌ (image)Freehand, fast for whiteboard-styleOne-off sketches; not for anything maintained

This track’s pattern (chosen deliberately): author canonical architecture diagrams in D2, commit both the .d2 source and the rendered .svg, and provide a Mermaid equivalent inline so the diagram renders even with no toolchain. You get diff-able source, a pretty render, and GitHub-native display.

Minimal D2 (the actual syntax used in this track’s files):

direction: right
api: "API Gateway\n[Container: Go]" { style.fill: "#438dd5" }
kafka: "orders\n[Kafka topic]" { shape: queue }
worker: "Order Worker\n[Container: Rust]" { style.fill: "#438dd5" }
api -> kafka: "produces"
kafka -> worker: "consumes (pull)"
Terminal window
d2 architecture.d2 architecture.svg # render
d2 --watch architecture.d2 # live-reload in the browser while editing

C4 covers structure. Most other questions are better answered by a different standard diagram — and Mermaid does all of these inline:

QuestionDiagramMermaid type
What are the moving parts?C4 Containergraph / flowchart
In what order do services talk during a request?SequencesequenceDiagram
What states can an order be in?State machinestateDiagram-v2
What’s the data shape?Entity-relationshiperDiagram
What’s the release timeline?Ganttgantt
How does work fan out across queues?Flow / C4flowchart

Sequence diagram — the request path through Streamflow (answers “what order, and where can it fail?”):

sequenceDiagram
    participant C as Customer
    participant API as API Gateway
    participant K as Kafka (orders)
    participant W as Order Worker
    participant DB as PostgreSQL
    C->>API: POST /orders
    API->>K: produce order.created (key=order_id)
    API-->>C: 202 Accepted
    K->>W: poll() batch
    W->>DB: persist state (tx)
    W->>K: produce settlement.requested + commit offset

State machine — the lifecycle the worker enforces (great for ## Key ideas that involve transitions):

stateDiagram-v2
    [*] --> Created
    Created --> Validated: passes checks
    Created --> Rejected: fails checks
    Validated --> Settling: settlement.requested
    Settling --> Settled: gateway captured
    Settling --> Failed: gateway declined
    Failed --> Settling: retry (backoff)
    Failed --> DLQ: max retries
    Settled --> [*]

4. Embedded, living models — diagrams that don’t rot

Section titled “4. Embedded, living models — diagrams that don’t rot”

A diagram is only worth drawing if it stays true. Make models embedded (in the repo, beside the code) and living (kept current by process, ideally by automation):

  • Co-locate: diagrams/*.d2 lives in the repo, not a wiki. The diagram moves with the code it describes.
  • Render in CI: a job runs d2 *.d2 (or mmdc) and fails if a .d2 no longer compiles, or regenerates SVGs as a release artifact. The diagram can’t silently break.
  • Review as code: an architecture change is a diff to a .d2 file in the same PR as the code change. Reviewers see the structural change.
  • Generate level 4 from code rather than maintaining it — ERDs from the schema, call graphs from the source. Hand-maintained code-level diagrams always drift; treat them as build output.
  • Date and own: every embedded diagram carries a comment header saying what question it answers and when it was last validated (see the .d2 files in this track).

This is the same insight as docs-as-code (topic 02): the artifact survives because it’s treated like source, not like a deliverable that’s “done.”

TechniqueWhen to apply
C4 Context (L1)Onboarding docs, exec/cross-team comms, any new project’s README
C4 Container (L2)Design docs, the default “how does it work” diagram, capacity/ops planning
C4 Component (L3)Only for a container with non-obvious internals worth explaining
Generate L4 from codeERDs, class diagrams — never hand-maintain
Mermaid inlineAnything reviewers should see without a render step
D2 + committed SVGCanonical, presentation-grade architecture diagrams
Sequence diagramTracing a request; debugging “who calls whom when”
State machineDocumenting a lifecycle/FSM (orders, jobs, sagas)
Render-in-CIAny diagram that must not be allowed to rot

In the course you draw your own system the way this topic draws Streamflow: context, containers, and the engine’s components in D2, rendered in CI, with every container of the system map present.

ModuleTopicKindPass
craft.09C4 diagrams in D2practice11
#ModuleChapterKindPass
1craft.09C4 diagrams in D2practice11
ConceptConnected TrackHow
Container/Deployment viewsCloud NativeThe L2/deployment diagrams are what you deploy to K8s
Sequence & data flowSystem DesignSystem design interviews are live diagramming under time pressure
Diagrams-as-code, review-as-codeSoftware CraftsmanshipSame “treat artifacts like source” principle as docs and tests
ER diagrams from schemaStorage & WarehousingGenerate, don’t draw, the data model
CompanyHow This AppearsFocus
GoogleDesign docs are mandatory and diagram-heavy; readability cultureContext + Container diagrams in every design doc
Amazon“Working backwards” docs, architecture review with C4-style viewsClear ownership boundaries
AnthropicThoughtful design docs for safety-critical systemsLegible, reviewable architecture
StripeFamous for documentation and API diagramsSequence diagrams for request flows
Any Staff+ roleYou are expected to communicate architecture, not just build itDiagramming as a core leadership skill