Diagrams as Code

Diagrams as code keeps architecture pictures in plain text next to the source they describe. This page shows the notation, one diagram written in five tools, a CI rendering setup, and honest pros and cons.


1. What It Is

Diagrams as code means writing a diagram as plain text — boxes, arrows and labels in a small language — and letting a tool draw the picture. The text lives in git next to the code it describes. Engineers write and review it; anyone reading documentation sees the rendered image.

The main tools are Mermaid (renders natively in GitHub, GitLab and many wikis), PlantUML (the broadest UML coverage), Structurizr DSL (C4 architecture models), D2 (modern syntax and layouts) and Graphviz (the original DOT language, still behind many tools).

2. Notation Legend

Legend: text syntax to rendered shapes A[Web App] renders as node / box A --> B renders as directed edge A -.-> B renders as dashed edge (async) B[(Database)] renders as storage / cylinder Web App Database

Every tool has its own syntax, but the ideas are the same: declare nodes with labels, connect them with edges, and attach style hints such as shape or line style. The legend uses Mermaid syntax. Layout — where boxes go — is decided by the tool's algorithm, not by you. That is the biggest difference from drawing tools.

Read the legend left to right: the text on the left is what you type, the shape in the middle is what the renderer draws, and the label on the right names the concept. Square brackets give a box its label, arrows connect node identifiers, and special bracket pairs pick shapes — [( )] is a cylinder for storage. Identifiers such as A stay short; labels carry the readable names.

3. Example 1: Online Shop Checkout

Example 1: online shop checkout (rendered) Customer browser Shop API checkout Payment service card charge Orders DB PostgreSQL Email queue async Card network Dashed arrow = asynchronous message; colours mark external and storage nodes.

Walkthrough. The customer calls the Shop API. The API charges the card through the payment service, which talks to the card network, and writes the order to the database. A dashed arrow shows the order event going to an email queue asynchronously. Below, the same diagram is written in all five tools so you can compare them.

Mermaid (renders in GitHub/GitLab Markdown, the Mermaid CLI mmdc, MkDocs, Docusaurus):

flowchart LR
  C[Customer] --> API[Shop API]
  API --> PAY[Payment service]
  PAY --> NET[Card network]
  API --> DB[(Orders DB)]
  DB -.-> Q[Email queue]

PlantUML (Java renderer, or a PlantUML server; widely supported in Confluence, IntelliJ and VS Code):

@startuml
actor Customer
component "Shop API" as API
component "Payment service" as PAY
database "Orders DB" as DB
queue "Email queue" as Q
Customer --> API
API --> PAY
API --> DB
DB ..> Q
@enduml

Structurizr DSL (rendered by Structurizr Lite or exported to PlantUML/Mermaid). Notice it defines a model first and then views of it:

workspace {
  model {
    customer = person "Customer"
    shop = softwareSystem "Online Shop" {
      api = container "Shop API"
      db  = container "Orders DB" "" "PostgreSQL" "Database"
    }
    pay = softwareSystem "Payment service"
    customer -> api "Checks out"
    api -> pay "Charges card"
    api -> db "Writes order"
  }
  views {
    container shop { include * autolayout lr }
  }
}

D2 (the d2 CLI; supports several layout engines):

Customer -> Shop API
Shop API -> Payment service
Payment service -> Card network
Shop API -> Orders DB
Orders DB -> Email queue: {style.stroke-dash: 4}
Orders DB.shape: cylinder

Graphviz DOT (the dot command):

digraph shop {
  rankdir=LR;
  Customer -> "Shop API";
  "Shop API" -> "Payment service" -> "Card network";
  "Shop API" -> "Orders DB";
  "Orders DB" [shape=cylinder];
  "Orders DB" -> "Email queue" [style=dashed];
}

4. Example 2: Rendering in CI and Docs

Example 2: diagrams rendered in CI docs/*.mmd, *.puml in git Pull request diff the text CI job mmdc / plantuml / d2 SVG files artifacts Docs site Reviewers comment on diagram changes line by line, like code.

Walkthrough. Diagram sources sit in a docs/ folder. On a pull request, reviewers see a text diff ("added an arrow from API to cache"). A CI job renders each file to SVG and publishes it with the docs. A minimal GitHub Actions step:

- name: Render diagrams
  run: |
    npm install -g @mermaid-js/mermaid-cli
    for f in docs/diagrams/*.mmd; do
      mmdc -i "$f" -o "${f%.mmd}.svg"
    done

Many teams skip rendering for Mermaid entirely, because GitHub and GitLab render ```mermaid blocks inside Markdown files automatically.

5. Tool Comparison

ToolBest forRenders whereNotes
MermaidFlowcharts, sequence, ER, state, quick docsGitHub, GitLab, many wikis, browser JSLowest friction; limited layout control
PlantUMLFull UML set, sequence diagramsJava or server; IDE plugins, ConfluenceMost diagram types; older-looking default style
Structurizr DSLC4 architecture models with many viewsStructurizr Lite / cloud, exportersModel once, many consistent views
D2Polished architecture diagramsd2 CLI, playgroundModern syntax, themes, multiple layout engines
GraphvizGenerated graphs (dependencies, pipelines)dot CLI, libraries in most languagesGreat auto-layout; plain styling

6. Pros and Cons

Pros

Cons

When to use: architecture, sequence, ER and flow diagrams that live alongside code and change with it. When not to: early brainstorming, one-off presentation graphics, or pixel-precise layouts — use a drawing tool such as draw.io or Excalidraw there.

A practical tip: keep each diagram small and focused on one question ("what happens at checkout?"), and split large systems into several views. Small diagrams lay out well automatically, and a reviewer can spot when one has gone stale.

8. Further Reading