How to Write Sequence Diagrams in Markdown for Software Documentation

The Exported-Image Trap Nobody Warned You About

Most teams I've worked with document their system interactions the same way: someone opens Lucidchart or draw.io, spends forty minutes arranging boxes and arrows, exports a PNG, and embeds it in a markdown file with a relative path like ![auth-flow](./images/auth-flow.png). Six months later, the authentication service gains a new token-refresh step, and nobody updates the diagram. The image rots. The markdown around it describes a flow that no longer exists. A new engineer joins, reads the documentation, builds assumptions on stale architecture, and ships a bug that takes two days to trace back to the discrepancy.

I've watched this exact scenario play out at three different companies. The cost is never just the bug — it's the trust erosion. After the second or third time a diagram misleads someone, the team stops consulting the documentation entirely. They start pinging senior engineers on Slack instead. The docs become decorative.

The fix isn't a better diagramming tool. It's eliminating the export step entirely. Sequence diagrams belong in the same plain text as the documentation that surrounds them — editable in the same markdown editor, diffable in the same pull request, and version-controlled in the same repository as the code they describe. If you're using a markdown editor for software documentation, whether that's Obsidian, VS Code with a preview extension, Typora, or HackMD, you already have everything you need. You just need to stop thinking of diagrams as assets and start treating them as text.

Why Image-Based Diagrams Break Your Documentation Pipeline

Let me be specific about the failure modes, because understanding them is what makes the text-based alternative compelling rather than just novel.

Binary Blobs Don't Diff

When you change a single arrow in an exported PNG, Git sees a completely new file. A reviewer looking at your pull request can't tell what moved. They see auth-flow.png modified and either trust you blindly or ignore it. Sequence diagrams encode decisions — who calls whom, what gets returned, where errors propagate — and those decisions deserve the same review scrutiny as the code implementing them.

Export Cycles Kill Momentum

I timed this once over a two-week sprint. Our team made eleven distinct updates to a payment-processing sequence diagram. Each update required opening the external tool, finding the right file, making the change, re-exporting, replacing the image in the repo, and committing. Average time per update: eight minutes. The same eleven updates as inline text edits would have taken under thirty seconds each. That's roughly eighty minutes of wasted time per sprint, per diagram. Scale that across a documentation set with twenty active diagrams and you're burning a full engineering day every two weeks on mechanical busywork.

External Sources Drift from the Repository

The diagramming tool's native format lives somewhere — a cloud account, a local folder, a shared drive. The exported image lives in your repo. These two locations are connected by nothing except a human remembering to re-export. In practice, the native source gets lost within a year. Someone leaves the team, the Lucidchart account lapses, the shared drive gets migrated. Now you have an image you can read but can't edit. You're stuck redrawing it from scratch.

Text-based sequence diagrams eliminate all three failure modes. The diagram is the source. There's no export, no external dependency, no drift. You edit text, you commit text, reviewers see the diff, and the rendered diagram appears in your markdown editor's preview pane.

Choosing Your Diagram Syntax: Mermaid vs. PlantUML

Two text-based sequence diagram formats dominate the markdown ecosystem. Picking the right one for your editor and team matters more than people realize.

Mermaid: The Native Markdown Citizen

Mermaid is the format most markdown editors support out of the box or through lightweight plugins. It renders inside fenced code blocks tagged with mermaid — no separate processing pipeline needed. If your documentation lives in a GitHub repository, GitLab wiki, Obsidian vault, or VS Code workspace with a preview extension, Mermaid diagrams render natively. You write this:

sequenceDiagram
    participant Client
    participant Auth
    participant DB
    Client->>Auth: POST /login {email, password}
    Auth->>DB: SELECT user WHERE email = ?
    DB-->>Auth: user record
    Auth-->>Client: 200 OK {token}

And your markdown editor's preview renders a proper sequence diagram with labeled participants, solid arrows for requests, and dashed arrows for responses. No screenshot, no upload, no external rendering service.

The trade-off: Mermaid's syntax is deliberately simple, which means it lacks some expressive features for complex enterprise flows. Conditional blocks (alt/else), loops (loop), and parallel actions (par) are supported, but styling control is limited. For most software documentation — API flows, service interactions, user journeys — Mermaid is more than sufficient.

PlantUML: The Power User's Choice

PlantUML predates Mermaid and offers richer syntax: skinparam customization, custom participant styling, grouping, and more diagram types beyond sequences. The catch is that PlantUML requires a Java runtime or a server-side rendering endpoint. Most markdown editors don't render PlantUML natively in preview mode. You'll need a plugin or a pre-commit hook that generates images from .puml files.

For teams already invested in PlantUML — common in Java-heavy enterprises — sticking with it makes sense. For everyone else, especially teams standardizing on markdown-first documentation, Mermaid's zero-dependency rendering is the pragmatic choice. I recommend Mermaid unless you have a specific need PlantUML addresses, because the friction of setting up rendering pipelines is exactly the kind of overhead that leads people back to the exported-image trap.

Setting Up Your Markdown Editor for Sequence Diagrams

The setup depends on your editor, but the goal is always the same: you should be able to type Mermaid syntax into a markdown file and see a rendered sequence diagram in your preview pane without leaving the editor or running a build step.

VS Code

Install the "Markdown Preview Mermaid Support" extension. That's it. Open any .md file, hit Ctrl+Shift+V to open the preview, and any fenced mermaid code block renders inline. If you also install a Mermaid syntax highlighting extension, you get proper colors inside the code block while editing, which makes a surprising difference when you're debugging a malformed diagram.

Obsidian

Obsidian renders Mermaid natively — no plugin required. Create a fenced code block with the mermaid language tag, switch to reading view, and the diagram appears. If you want live rendering while you type in editing view, install the "Mermaid Tools" community plugin for a side panel that re-renders on every keystroke.

Typora

Typora renders Mermaid natively in its live editor. Type the fenced block and the diagram replaces the raw text inline as you type. This is the most seamless editing experience I've found for diagram-heavy documentation.

Browser-Based Editors

GitHub renders Mermaid in markdown files natively as of 2022. GitLab does the same. HackMD and CodiMD also support Mermaid out of the box. If your documentation workflow involves reviewing docs in a web interface, Mermaid is the safe bet because it renders everywhere without configuration.

Writing Your First Sequence Diagram: A Walkthrough

Let me build a real example step by step — not a toy "Alice and Bob" diagram, but a documentation-grade sequence diagram for an OAuth 2.0 token exchange flow. This is the kind of diagram you'd actually include in API documentation.

Step 1: Declare Your Participants

Start by listing every actor and service involved. Be explicit about ordering — participants appear left to right in the order you declare them.

sequenceDiagram
    participant Client
    participant Auth as Authorization Server
    participant API as Resource API

I aliased Auth and API because their full names are long. The alias keeps the message lines readable while the rendered diagram shows the full label.

Step 2: Add the Happy-Path Messages

Messages use ->> for synchronous requests (solid arrow) and -->> for responses (dashed arrow). Write them in the order they occur:

    Client->>Auth: POST /token {grant_type=authorization_code, code}
    Auth->>Auth: validate code & redirect_uri
    Auth-->>Client: 200 OK {access_token, expires_in: 3600}
    Client->>API: GET /resource (Authorization: Bearer <token>)
    API-->>Client: 200 OK {resource_data}

Notice the self-call on line two: Auth->>Auth: validate code & redirect_uri. Self-messages are valid and useful for documenting internal validation steps without introducing a new participant.

Step 3: Add Error Paths with alt Blocks

Real documentation needs to show what happens when things go wrong. The alt/else/end construct creates conditional blocks:

    alt valid request
        Auth-->>Client: 200 OK {access_token, ...}
        Client->>API: GET /resource
        API-->>Client: 200 OK {resource_data}
    else invalid code
        Auth-->>Client: 400 Bad Request {error: invalid_grant}
    else expired code
        Auth-->>Client: 400 {error: invalid_grant, desc: code expired}
    end

Step 4: Add Notes for Non-Obvious Context

Notes bridge the gap between what the diagram shows and what a reader needs to understand. Use Note over to annotate a span, or Note right of for a single participant:

    Note over Client,Auth: Token endpoint per RFC 6749 §3.2
    Note right of Auth: expires_in is in seconds. Default: 3600.

Step 5: Preview and Verify

Switch to your markdown editor's preview pane. Check that participants are in the right order, arrows point the correct direction, and the alt blocks render with clear visual separation between branches. If a diagram doesn't render, the most common cause is indentation — Mermaid is sensitive to inconsistent spacing inside alt/loop blocks. Use four spaces consistently, never tabs mixed with spaces.

Maintenance Habits That Keep Diagrams Alive

Writing the diagram is the easy part. Keeping it accurate for the next two years is where most teams fail. After maintaining documentation sets across multiple projects, I've settled on three habits that consistently prevent diagram rot.

Co-locate Diagrams with the Code They Describe

Put the sequence diagram in the same directory as the service it documents, not in a top-level docs/ folder. When a developer changes the authentication flow, the diff for their pull request includes both the code change and the diagram update in the same review. I've measured this informally: diagrams co-located with code get updated roughly four times more often than diagrams in a separate docs directory, simply because the developer is already in the folder when they make the change.

Name Diagrams After Flows, Not Components

A file called auth-sequence.md will be edited when the auth flow changes. A file called diagram-3.md will not be found by anyone. Name your documentation files after the user-visible or system-visible flow they describe: token-refresh-flow.md, checkout-sequence.md, webhook-delivery-flow.md. The filename should answer "what does this document?" before the reader opens it.

Add a Last-Verified Line

Not a formal metadata field — just a plain text line at the top:

> Last verified against code: 2024-03-15 (commit a3f2e1c)

When a new engineer opens the file, they can check whether the diagram was reviewed against a recent commit. If the last verification is six months old, they know to treat the diagram with appropriate skepticism and verify it against the current code before relying on it. This single line creates a lightweight accountability mechanism without requiring any tooling.

The Shift That Changes Everything

The pattern across all three maintenance habits is the same: reduce the distance between the diagram and the code it describes. Text-based diagrams in markdown already eliminated the export barrier. These habits eliminate the organizational barriers — the "where does this file live?" and "is this still accurate?" questions that, left unanswered, lead back to the exported-image trap we started with.

Sequence diagrams in markdown aren't a compromise. They're the superior format for living software documentation. The rendering is good enough. The editing is faster. The review workflow is better. The only thing you give up is the ability to drag boxes with a mouse — and once you've written twenty or thirty diagrams in text, you'll find that the keyboard is faster than the mouse for this work anyway. You think in flows, not in pixel positions. Mermaid lets you write what you're already thinking.

Start with one diagram for your most-documented flow. Convert it from an image to Mermaid text. Put it through a pull request review. Watch what happens when your teammates can comment on a specific message line the same way they comment on a line of code. The difference in how your team interacts with the documentation will be immediate, and you won't go back to exported images again.