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:

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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