Topic: Mermaid diagrams in ADRs
Mermaid Diagrams in ADRs — Embedding Sequence Diagrams, C4 Models, and Flowcharts in Markdown
Mermaid is text-as-code for diagrams: committed to the same git repository as the ADR, versioned alongside the decision it illustrates, diffable in pull requests. GitHub and GitLab render Mermaid natively — no build step, no external tool, no link that breaks when a Miro board is deleted. This page explains which Mermaid diagram types belong in an ADR and when, with copy-paste examples for the four diagram types engineers use most in architecture decisions.
TL;DR
Embed Mermaid in your ADR's Markdown file when the diagram is small enough to be read as source text and the interaction pattern is the decision (sequence diagrams for API contracts; C4 context for service boundaries). Commit a PNG when the diagram is too complex for readable source text. Use Mermaid as the committed record of the architecture, and Miro or FigJam as the whiteboard exploration surface — they are complementary phases, not competing tools. The diagram in the ADR file is the one that survives: it's under git history, it diffs, it renders on GitHub without any external tool, and it doesn't expire.
Why Mermaid specifically — the text-as-code advantage
Architecture diagrams in external tools (Miro, Lucidchart, draw.io) share a durability problem: they live at a URL. When the board is deleted, the workspace lapses, or the link permissions change, the ADR that references the diagram becomes incomplete. The reasoning is still there in prose but the visual is gone — exactly when the next engineer most needs it.
Mermaid solves this by making diagrams first-class text. The diagram source is a fenced code block inside the Markdown ADR file:
```mermaid
sequenceDiagram
Client->>AuthService: POST /token (username, password)
AuthService->>Database: SELECT user WHERE email = ?
Database-->>AuthService: user row (hashed password)
AuthService->>AuthService: verify bcrypt hash
AuthService-->>Client: 200 { access_token, refresh_token }
```
GitHub renders this inline. GitLab renders it inline. VS Code with the Markdown Preview Enhanced extension renders it locally. The Mermaid source is committed alongside the ADR file in the same pull request — so the diagram version is tied to the decision version in git history. When the ADR is superseded, the diagram in the superseded file remains exactly as it was when the decision was made. No broken links.
The trade-off: Mermaid requires a Markdown renderer that supports it. Email clients, PDF exports, and older GitHub Enterprise instances don't render Mermaid — the diagram source appears as a code block. The mitigation is always to write the decision fully in prose first, with the diagram as a supplement that adds visual clarity to what the prose already explains. An ADR that depends on the diagram to be understood is an ADR whose prose is insufficient.
The four Mermaid diagram types that belong in ADRs
1. Sequence diagrams — for API contracts and interaction decisions
Sequence diagrams are the most consistently useful Mermaid type in ADRs. An architecture decision about API contract, message ordering, or service interaction pattern is precisely what a sequence diagram captures. The decision isn't just "use JWTs for authentication" — it's "the client presents the JWT in the Authorization header on every request, and the API gateway validates the signature before forwarding." That interaction pattern is a decision, and a sequence diagram is the densest way to record it.
```mermaid
sequenceDiagram
participant C as Client
participant GW as API Gateway
participant S as Service
C->>GW: GET /resource (Authorization: Bearer <jwt>)
GW->>GW: verify JWT signature (public key)
GW->>S: GET /resource (X-User-ID: <sub>)
S-->>GW: 200 { resource }
GW-->>C: 200 { resource }
```
The rejected alternative — session cookies validated against a shared Redis store — would have a different sequence diagram. Including both (the chosen and the primary rejected alternative) in the ADR's Considered Options section is a high-value use of Mermaid: the reader can immediately see the structural difference, not just read about it.
2. C4 context diagrams — for service boundary decisions
Service boundary decisions ("should payment processing be a separate service or part of the Orders service?") are the hardest ADR type to communicate in prose alone. A C4 context or container diagram makes the boundary explicit. Mermaid supports C4 natively:
```mermaid
C4Context
title Order + Payment Boundary Decision
Person(customer, "Customer")
System(orders, "Orders Service", "manages cart, orders, fulfilment")
System(payments, "Payments Service", "processes charges via Stripe")
System_Ext(stripe, "Stripe", "payment gateway")
Rel(customer, orders, "places order")
Rel(orders, payments, "POST /charges {amount, currency, card_token}")
Rel(payments, stripe, "API call")
```
The rejected alternative — keeping payment processing inside the Orders service — would show Stripe being called directly from Orders. The boundary decision becomes visually immediate. For a decision with multi-year architectural implications, this is exactly the kind of artifact that makes the ADR genuinely useful to the next engineer inheriting the codebase.
3. Flowcharts — for decision-tree reasoning in the Context section
The Context section of an ADR describes the forces and constraints that shaped the decision space. When the constraints are themselves conditional ("if team size exceeds 20, then X; otherwise Y"), a flowchart can make the decision landscape legible without burying it in conditional prose.
```mermaid
flowchart TD
A[Database decision trigger] --> B{Team size?}
B -->|<10 engineers| C{Operational complexity budget?}
B -->|≥10 engineers| D[PostgreSQL — team has DBA bandwidth]
C -->|Minimal| E[SQLite — single binary, zero ops]
C -->|Moderate| F[PostgreSQL — more headroom for growth]
```
Note that a flowchart in the Context section illustrates the constraints that led to the decision space — it is not the decision itself. The Decision section should still state the chosen option in a single active-voice sentence. The flowchart supplements the Context; it doesn't replace the Decision.
4. State diagrams — for lifecycle and state-machine decisions
Decisions about entity lifecycle ("what are the valid states of an Order, and which transitions are allowed?") are well-suited to state diagrams. The state machine is the decision — the allowed transitions are explicit, the forbidden transitions are implicit by their absence.
```mermaid
stateDiagram-v2
[*] --> Pending: order placed
Pending --> Confirmed: payment captured
Pending --> Cancelled: payment failed / user cancels
Confirmed --> Shipped: fulfilment picks item
Confirmed --> Cancelled: item out of stock (refund issued)
Shipped --> Delivered: carrier confirms delivery
Shipped --> ReturnRequested: customer initiates return
Delivered --> ReturnRequested: within 30 days
ReturnRequested --> Refunded: return received and inspected
Cancelled --> [*]
Refunded --> [*]
Delivered --> [*]
```
This is especially valuable for decisions that evolve over time. When the ADR is later annotated (per the how-to-update-an-adr protocol), adding a new transition ("ReturnRequested → Rejected: return condition unacceptable") to the state diagram is a legible one-line diff — the kind of change a reviewer can verify against the Decision section update in the same PR.
When NOT to use Mermaid in an ADR
Mermaid is not the right tool for every architecture diagram in an ADR:
- Whiteboard-stage exploration. During an RFC session or a live team design discussion, Miro or FigJam is a better surface: real-time collaboration, freeform sticky notes, live participant annotations. At this stage the diagram is a thinking tool, not a record. Export the Miro frame as a PNG and commit it alongside the ADR once the decision is made — see the ADR template in Miro page for the export protocol.
- Highly complex diagrams. A sequence diagram with 20+ participants, or a C4 container diagram with 40+ systems, produces Mermaid source that is harder to read than the rendered image. At this scale, commit a PNG generated from the Mermaid source using
mermaid-js/mermaid-cli(mmdc) and link to it from the ADR. Keep the Mermaid source in a companion.mmdfile in the same directory so it's recoverable and diffable. - Diagrams that will be consumed outside GitHub/GitLab. If your ADR review process involves email, Confluence page copies, or PDF exports, embedded Mermaid won't render. Write the decision fully in prose; include the diagram only as supplemental context.
- Infrastructure topology maps. Tools like Diagrams-as-code (Python's
diagramslibrary), structurizr, or AWS's CDK Diagram are better suited to physical infrastructure — they have native support for cloud provider icons and the topology shapes that infrastructure decisions require. Mermaid's C4 support covers the logical boundary layer; it does not replace dedicated infrastructure diagramming tools for the physical layer.
CI validation — catching Mermaid syntax errors in PRs
Mermaid syntax errors produce a silent empty diagram in the GitHub renderer — no error message, just a blank space where the diagram should be. This makes syntax errors easy to miss in review. The mermaid-js/mermaid-cli package (mmdc) can render Mermaid source to SVG in a CI job, which catches syntax errors before merge.
Adding a Mermaid validation step to your ADR GitHub Action:
- name: Validate Mermaid diagrams in ADRs
run: |
npm install -g @mermaid-js/mermaid-cli
# Extract all mermaid code blocks from changed ADR files
for f in $(git diff --name-only origin/main...HEAD -- 'doc/decisions/**/*.md'); do
awk '/^```mermaid$/,/^```$/' "$f" | \
grep -v '^```' | \
mmdc -i /dev/stdin -o /dev/null 2>&&1 \
|| (echo "Mermaid syntax error in $f" && exit 1)
done
This job runs mmdc on each Mermaid block in the changed ADR files and exits non-zero if any block fails to parse. It does not validate diagram semantics — it only catches syntax errors that would produce a blank diagram in the renderer. The job takes approximately 15–30 seconds on a standard GitHub Actions runner; add it after the heading-lint job and before the merge step.
The --puppeteer-config flag is required in sandboxed environments (GitHub Actions runs in a container without a full Chromium sandbox). Use the following puppeteer-config.json:
{
"args": ["--no-sandbox", "--disable-setuid-sandbox"]
}
Mermaid vs PNG vs Miro — the three layers in an ADR
The three approaches are not mutually exclusive — they serve different phases of the ADR lifecycle:
| Approach | Phase | Durability | Diffability | Rendering | Best for |
|---|---|---|---|---|---|
| Mermaid (embedded) | Committed record | High — in git forever | High — text diff | GitHub, GitLab, VS Code; not email/PDF | Sequence diagrams, small C4 context, state machines |
| PNG (committed) | Committed record | High — in git forever | Low — binary blob | Universal — everywhere Markdown renders | Complex diagrams, infrastructure topology, Miro exports |
| Miro (linked) | Exploration / RFC | Low — link breaks when board deleted | n/a — external tool | Miro only | Real-time collaboration, whiteboard sessions, sticky-note clustering |
The recommended workflow for teams that use Miro during the RFC stage and Mermaid for the committed record:
- Phase 1 — RFC exploration in Miro. Run the collaborative whiteboard session. Cluster options, vote with dot stickers, capture constraints as sticky notes. The Miro board is the deliberation surface.
- Phase 2 — AI-assisted deliberation. Move the finalized options from Miro to Claude.ai or ChatGPT for structured trade-off reasoning. The AI conversation produces the raw material for the ADR's Context and Decision sections.
- Phase 3 — ADR authoring. Write the ADR Markdown file with the prose decision and embed a Mermaid diagram of the chosen architecture. Export the Miro exploration frame as a PNG and commit it as a supplemental image if the whiteboard contained detail the Mermaid diagram doesn't capture. Export the Miro board before closing the session — Miro public links break without warning.
- Phase 4 — Decision extraction. Export the Claude.ai or ChatGPT conversation from Phase 2. Run the WhyChose extractor to surface the structured decision record from the AI deliberation, including the rejected alternatives and trade-offs. Merge the extracted record into the ADR's Alternatives Considered section.
Copy-paste ADR template with Mermaid section
A Nygard-format ADR with a Mermaid diagram in the Context section:
# ADR-NNNN: <Decision title — active voice statement of the choice made>
## Status
Proposed
## Context
<Describe the forces and constraints that shaped the decision space. Include
the Mermaid diagram here if the problem space benefits from a visual.>
```mermaid
sequenceDiagram
<diagram source>
```
## Decision
We chose <option> over <primary alternative> because <core reason>.
## Alternatives considered
### <Primary alternative>
<Why it was rejected.>
### <Secondary alternative>
<Why it was rejected.>
## Consequences
<What becomes easier, what becomes harder, what new obligations arise.>
Include at least one negative consequence — an all-positives Consequences
section is the most common sign of incomplete ADR reasoning.
For MADR format, embed the Mermaid diagram in the "Technical Story" or "Considered Options" sections. The MADR frontmatter is unaffected by diagram blocks in the body.
Related questions
Does GitHub render Mermaid in ADR Markdown files?
Yes — GitHub has rendered Mermaid diagrams natively in Markdown files since May 2022. Any Markdown file in a GitHub repository containing a fenced code block tagged with 'mermaid' renders the diagram inline. GitLab has supported native Mermaid rendering since 2018. No build step, no external tool, no CDN dependency — the Mermaid library is served by the GitHub/GitLab frontend. GitHub Enterprise instances may lag the cloud version; check your instance's GitHub Enterprise release notes for the Mermaid version included. VS Code renders Mermaid via the "Markdown Preview Enhanced" or "Mermaid Preview" extensions for local development.
What Mermaid diagram type is most useful in an ADR?
Sequence diagrams are the most consistently useful across all ADR types: they capture API contracts, service interaction order, and async message flows — the exact artifacts an architecture decision about service boundaries produces. C4 context diagrams are the second most useful: they show system boundaries and external actor relationships, which map directly to service-boundary decisions. Flowcharts fit the Context section when the constraints are branchy. State diagrams fit lifecycle decisions. The correct question is "what did this decision actually determine?" — the answer dictates the diagram type. If the decision determined who calls whom in what order, use a sequence diagram. If it determined where a service boundary sits, use C4. Don't embed a diagram for its own sake.
Should you embed Mermaid diagrams in ADRs or link to image files?
Embed Mermaid when the diagram is small enough to be read as source text and stable enough that future diffs are meaningful. Commit a PNG when the diagram is too complex for readable source text (roughly over 30 nodes/edges), or when the ADR will be consumed in contexts where Mermaid doesn't render (email, PDF, older GitHub Enterprise). The two approaches can coexist: embed a simplified Mermaid overview and link to a detailed PNG for full fidelity. If you commit a PNG, also commit the Mermaid source as a companion .mmd file so future maintainers can update the diagram without having to recreate it from scratch in an external tool.
Can you use Mermaid diagrams in MADR-format ADRs?
Yes. MADR (versions 3 and 4) does not constrain the content within its section headings — it defines the required headings (Title, Status, Context and Problem Statement, Decision Drivers, Considered Options, Decision Outcome, Pros and Cons of the Options) but leaves the prose within each section to the author. Mermaid blocks can be embedded anywhere in the document body. A C4 context diagram in "Context and Problem Statement" showing the current architecture is a common and effective use. A sequence diagram per option in "Considered Options" makes the structural difference between options immediately legible. The YAML frontmatter (status, deciders, informed, date) is rendered separately by GitHub and does not conflict with Mermaid blocks in the document body.
Further reading
- ADR template in Miro — whiteboard sessions and the diagram handoff — the exploration-phase complement to Mermaid-as-committed-record. Miro and FigJam are the right surface for collaborative RFC sessions: real-time dot-sticker voting, sticky-note clustering, participant annotation. This page covers the Miro ADR frame template structure, Miro export formats and their durability (the public-link expiry problem), and the graduated four-phase handoff from Miro whiteboard → AI deliberation → Mermaid ADR → WhyChose extraction. Read alongside this page to see how the two tools fit into the same workflow at different stages.
- ADR template for GitHub repos — the folder layout, PR template, and branch conventions for storing Mermaid-equipped ADR Markdown files in a GitHub repository. Covers the
doc/decisions/path convention, how GitHub renders both the YAML frontmatter and Mermaid blocks in the same file, and the PR template that ensures reviewers check both the diagram and the prose for consistency before merging. - The ADR GitHub Action — a CI pipeline for architecture decision records — the four-job CI workflow that lints ADRs on every PR touching
doc/decisions/. The Mermaid syntax validation job described on this page extends job 1 of that pipeline. Adding Mermaid validation prevents the silent-empty-diagram failure mode where a syntax error produces a blank space rather than an error message. - ADR template in Markdown — copy-paste ready — the baseline Nygard and MADR templates in Markdown format, without any diagram conventions. Start here for the prose template, then add Mermaid blocks from the copy-paste examples on this page.
- ADR vs RFC — what's the difference and when should you use each? — the RFC is often where the Miro whiteboard session happens (exploration, open, seeking input); the ADR is where the Mermaid diagram is committed (record, closed, preserving rationale). Understanding the RFC-to-ADR handoff explains why the diagram in the RFC stage (Miro) and the diagram in the ADR (Mermaid) serve different purposes and should be different artifacts.
- The open-source extractor — reads your Claude or ChatGPT conversations.json export and surfaces the architecture decisions inside as structured ADR records. In the Phase 2 AI-deliberation workflow described on this page, the extractor produces the Decision and Alternatives Considered sections from the AI chat — the Mermaid diagram is the committed artifact you create in Phase 3 from the decision those AI conversations produced.