Skip to main content

Mermaid Diagramming

· 16 min read
Software Engineer

With the recent introduction of Mermaid support in GitHub Markdown, I wanted to explore Mermaid in some more detail. In this article I'll walk through my exploration of diagrams as code with Mermaid and show some detailed examples of practical Mermaid diagrams.

What is Mermaid?

Mermaid is a Javascript tool to create diagrams from text and code. It is can be embedded in markdown files and enables software architecture to be defined and version controlled along with code.

Diagrams as Code

Diagrams as Code is a a relatively new concept in which diagrams are defined as code or text and a visual diagram is generated from that text. The original text can be version controlled just like software using standard tooling like Git.

The concept was featured in the Thoughtworks Technology Radar vol. 23 in October 2020, which cites other tools such as Diagrams (a Python diagramming library), PlantUML, and GraphViz.

While there are a number of benefits to the diagrams as code concept – such as its lightweight nature, ease of version control, and ability to define architecture alongside code – it also has some downsides. Relative to GUI-based diagramming tools such as Diagrams.net (formerly Draw.io), LucidChart, or Visio, it is harder to define and layout our diagrams. Relative to more data-driven modeling tools such as MagicDraw/Cameo, Rhapsody, or Trivium – automation, and data-driven decision making are harder.

State of the Art and Beyond

Diagrams as code (DaC) does something very useful when combined with Markdown: it renders diagrams as part of documents. This is something that large enterprises experience as a pain point when defining more formal architectures with heavy-weight modeling tools.

This is a problem we've tried to address at Triple Dot Engineering with Trivium. What we've found is that the majority of our users want a graphical interface for drawing diagrams. And, relative to DaC, this is a much faster and more flexible way of creating diagrams. But a key problem with this approach is the inherent disconnect between diagrams and documents. This is something that DaC can solve – and GitHub's support for Mermaid is a big step in support of that.

At Triple Dot, we've broadly categorized these tools into two categories: diagramming tools (such as Diagrams.net, LucidChart, or Visio) and modeling tools (e.g. MagicDraw/Cameo, Enterprise Architect, or other UML/SysML tools). But I think diagrams as code is deserving of its own category. The table below provides a relative comparison of the capabilities of these categories.

Diagramming ToolsModeling ToolsDiagrams as Code
Intuitive / Low Learning Curve🟢🔴🔴
Data Driven / Queryable🔴🟢🟡
Collaborative🔴🔴🟡 / 🟢
Extensible / Flexible🟡🟡🟡

As of this writing, DaC isn't as widely used and how well it can perform in some of these areas is still unknown. But I suspect that will change.

Tools like Mermaid have some learning curve, but that should decrease over time – especially with wider adoption. While the data in diagrams may not be immediately queryable tools can be built around Mermaid (or other tools) that will support that.

At the risk of making a bad technology trend prediction, I don't think DaC will fully replace graphical diagramming tools. There will always be people in organizations that aren't comfortable writing code or using Git – I don't think that will change. But I also think we'll see much more use of DaC tools in the future. GitHub's support for Mermaid is a strong indicator that Mermaid will be the tool of choice in that space.

Mermaid Basics

As of this writing, Mermaid supports the following diagram types:

  • Graph / Flowchart
  • Sequence Diagram
  • Class Diagram
  • State Diagram
  • Entity Relationship Diagram
  • User Journey
  • Gantt
  • Pie Chart
  • Requirement Diagram

I won't detail each of those here. For detailed documentation, refer to the Mermaid documentation. The remainder of this article will go through a few practical examples where we can use Mermaid for engineering design communication, system architecture, and project management.

Graphs

As a simple example, we can create stylized graphs quite easily.

graph LR
A((A)) --> B((B))
B --> C((C))
B --> D((D))
D --> E((E))
E --> F((F))
F --> A
C --> F
C --> D

classDef NodeStyle fill:#bae6ff,stroke:#33b1ff,stroke-width:1px
classDef RedNode fill:#ffd7d9,stroke:#ff8389,stroke-width:1px
classDef GreenNode fill:#a7f0ba,stroke:#42be65,stroke-width:1px
classDef PurpleNode fill:#e8daff,stroke:#be95ff,stroke-width:1px
class A,D,E NodeStyle;
class B,C RedNode
class F GreenNode

Flowcharts

Similarly, we can adjust the shapes of elements in the graph to draw flow charts to communicate business process flows, algorithms, or system behaviors.

flowchart TD
A[Start] --> B{Should it move?};
B -- Yes --> SHOULD_MOVE{Does it move? };
SHOULD_MOVE -- No --> WD40(WD-40)
SHOULD_MOVE -- Yes --> END(Do Nothing);
B -- No --> SHOULD_NOT_MOVE{Does it move? };
SHOULD_NOT_MOVE -- Yes --> DUCT_TAPE(Duct Tape)
SHOULD_NOT_MOVE -- Yes --> END

Class

Mermaid supports a syntax for defining class diagrams that is not that far from writing pseudo-code.

classDiagram
class Course{
-int id
-string courseName
+addStudent(Student student)
+getStudents() List~Student~
}

class Student{
-String id
-String name
}

class Assignment{
int id
String title
}

class Instructor{
-String id
-String name
}

Student "1..*" *-- "1..*" Course
Course "1" *-- "0..*" Assignment
Course "1..*" *-- "1..*" Instructor

State

Mermaid also supports state diagrams. At this point, there are some limitations, but it works well for simple flows.

stateDiagram-v2
[*] --> TODO
TODO --> IN_PROGRESS
IN_PROGRESS --> TODO
IN_PROGRESS --> DONE
DONE --> IN_PROGRESS
DONE --> [*]

Sequence Diagram

We can use sequence diagrams to visually show how two systems communicate. The example below shows the classic client-server model.

sequenceDiagram
autonumber
Client->>Server: GET /index.html
Server-xClient: Here! "<html>...</html>"

Advanced Examples

Logical architecture

We can do more than simple flows with the flowcharts type, we can define much more complex architectures with subgraphs and custom styling. The example below shows a web application architecture with multiple subsystems.

flowchart LR

subgraph CLIENT
direction TB
_id_webapp(Web App)
_id_ios(Mobile iOS)
end

subgraph EDGE
direction TB
_id_serverless[Serverless Gateway] --> _id_gw_db[(GW Config DB)]
end

subgraph BACKEND
direction LR

subgraph Core
direction TB
_id_mcf_ga_blue(MCF GA Blue)
_id_mcf_ga_green(MCF GA Green)
_id_mcf_ga_canary(MCF GA Canary)
_id_mcf_ee_blue(MCF EE Blue)
_id_mcf_ee_green(MCF EE Green)
_id_mcf_ee_canary(MCF EE Canary)

end

_id_mcf_db[(MongoDB)]
_id_backup_util(Backup Util) --> _id_mcf_db

end

EDGE -.- BACKEND
CLIENT --> _id_serverless

_id_serverless --> _id_mcf_ga_blue --> _id_mcf_db
_id_serverless --> _id_mcf_ga_green --> _id_mcf_db
_id_serverless --> _id_mcf_ga_canary --> _id_mcf_db
_id_serverless --> _id_mcf_ee_blue --> _id_mcf_db
_id_serverless --> _id_mcf_ee_green --> _id_mcf_db
_id_serverless --> _id_mcf_ee_canary --> _id_mcf_db

classDef NodeStyle fill:#bae6ff,stroke:#33b1ff,stroke-width:1px
classDef GroupStyle fill:#f2f4f8,stroke:#c1c7cd,stroke-width:1px
classDef PurpleNode fill:#e8daff,stroke:#be95ff,stroke-width:1px
classDef GreenNode fill:#a7f0ba,stroke:#42be65
classDef Server fill:#9ef0f0,stroke:#08bdba

class CLIENT,EDGE,BACKEND NodeStyle
class Core GroupStyle
class _id_serverless PurpleNode
class _id_gw_db,_id_mcf_db GreenNode
class _id_mcf_ga_blue,_id_mcf_ga_green,_id_mcf_ga_canary,_id_mcf_ee_blue,_id_mcf_ee_green,_id_mcf_ee_canary Server

SysML BDDs

We can use the classDiagram to create SysML style Block Definition Diagrams (BDDs)

%%{init: {'theme':'base' }}%%
classDiagram
class System{
<<block>>
-name : string = "Space Shuttle"
}

class Structures {
<<block>>
}

class Propulsion{
<<block>>
-engine : string = "RS-25"
}

class ADCS{
<<block>>
-numReactionWheels : int = 4
}

class CDH{
<<block>>
}

class EPS{
<<block>>
-numBatteries : int
+getPowerPct() : int
}

System "1" *-- "*" Structures
System "1" *-- "3" Propulsion
System "1" *-- "1" ADCS
System "1" *-- "1" CDH
System "1" *-- "1" EPS

Activity Diagrams

As I was writing this article, I spent some time exploring if there was a way to draw something similar to a SysML Activity Diagram complete with swimlanes using Mermaid.

What I've found seems to be somewhat limiting, though I expect this is something we'll see support for eventually in Mermaid.

The diagram below is the closest I was able to come to a SysML activity diagram in the time I spent exploring Mermaid. It's not rendering the way I would like, but it does adequately convey the process flow.

stateDiagram-v2
direction TB

state Client {
direction TB

[*] --> doLogin
doLogin --> login
storeToken --> [*]
showError --> [*]
}

state API {
direction TB
login --> HandleAuth
respondOk
respondUnauthorized
}

state Cognito {
direction TB

state is_logged_in <<choice>>
HandleAuth --> is_logged_in
is_logged_in --> returnToken: yes
is_logged_in --> reject : no
returnToken
reject
}

respondOk --> storeToken
respondUnauthorized --> showError

returnToken --> respondOk
reject --> respondUnauthorized

Sequence Diagrams

We can also use actors and rectangles to alter the appearance of sequence diagrams. Here's a simple crytographic example.

sequenceDiagram
actor Alice
actor Eve
actor Bob

Alice->>Bob: Hi Bob
Bob->>Alice: Hi Alice

rect rgb(255, 235, 238)
alt Encrypted with ROT13
Alice->>Bob: {"data": "Uv Obo"}
Bob->>Alice: {"data": "Uv Nyvpr"}

end
end

Note over Alice, Bob: Alice and Bob can use a ROT13 cipher <br> to keep their conversation private

Requirements Diagram

The Mermaid requirementDiagram is fairly robust. It has support for a base requirement type, but also supports functionalRequirement, performanceRequirement, interfaceRequirement, physicalRequirement, and designConstraint.

While this certainly supports the basic functionality I would expect in a requirements diagram, without being able to parse the requirements and share them as a CSV or generate a Word document or PDF report, I expect the practical value here may be limited.

That said, it's not unreasonable that a tool could be developed to parse Mermaid requirements diagrams and output them in a more conventional format. Alternatively, Mermaid diagrams could be generated from existing requirements data and used only as a reporting tool.

requirementDiagram

requirement req_1 {
id: 1
text: The system shall fly.
risk: high
verifymethod: test
}

requirement req_1_1 {
id: 1.1
text: The system will be fixed wing.
risk: low
verifymethod: inspection
}

requirement req_1_2 {
id: 1.2
text: The system shall have a maximum wingspan of 48".
risk: medium
verifymethod: demonstration
}

element computer_analysis {
type: simulation
}


req_1 - contains -> req_1_1
req_1 - contains -> req_1_2
computer_analysis - satisfies -> req_1_2

Gantt Charts

Last, but certainly not least, Mermaid supports Gantt charts. There are some limits – such as not visualizing dependencies – and I can see where this can quickly become unruly for larger projects. But for small projects, this is a pretty powerful way to capture schedule information along with source code in a visual way.

%%{init: {'theme':'base', 'themeVariables': {'primaryColor': '#33b1ff', 'secondaryColor': '#08bdba', 'tertiaryColor': '#33b1ff'}}}%%
gantt
dateFormat YYYY-MM-DD
axisFormat %m/%d
title Sample GANTT Chart - NASA Project Lifecycle
todayMarker off

%% this is a comment
section Pre-Phase A
Project Kickoff :milestone, done, m0, 2022-01-10, 0d
Pre-Phase A Studies : done, item1, after m0, 38d
KDP A :milestone, done, kdpA, 2022-02-18, 0d


%% this is a comment
section Phase A
Phase A - Concept & Technology Development :active, ID-01, after kdpA, 21d
Concept Definition :active, ID002, after kdpA, 5d
Functional Decomposition :active, ID003, after ID002, 5d
Component Decomposition :active, ID004, after ID002, 5d
Requirements Definition : after ID003, after ID004, 5d
SRR :milestone, crit, srr, 2022-03-10, 0d
KDP B :milestone, kdpB, 2022-03-11, 0d

%% this is a comment
section Phase B
Phase-B - Preliminary Design : after kdpB, 44d
PDR :milestone, crit, pdr, 2022-04-20, 0d
KDP C :milestone, kdpC, 2022-04-26, 0d

section Phase C
Phase C - Final Design & Fabrication : IDC, after kdpC, 160d
CDR :milestone, crit, cdr, 2022-10-01, 0d
KDP D :milestone, kdpD, after IDC, 0d

section Phase D
Phase D - Assembly, I&T : IDD, after kdpD, 42d
ORR :milestone, crit, orr, 2022-10-21, 0d
KDP E :milestone, kdpE, after IDD, 0d

section Phase E
Phase E - Ops & Sustainment : IDE, after kdpE, 10d
KDP F :milestone, kdpF, after IDE, 0d

section Phase F
Phase F Closeout : after kdpF, 7d
End of Life :milestone, crit, EOL, 2022-12-03, 0d

Closing Thoughts

To render Mermaid diagrams on my site, I needed to add a Remark plugin that renders the Mermaid diagrams. Given the nature of this site (statically generated using Docusaurus), the remark-mermaid plugin didn't prove viable. I instead used remark-mermaid-dataurl (I have forked it here) which converts Mermaid code into url-based images designed to work with Docusaurus.

I also found a number of other tools that I did not have the time to explore, but hope to in the future such as a Mermaid Draw.io Plugin.

Going forward, I hope to explore the Mermaid library itself and the feasiblity of parsing Mermaid graphs for use with other analysis or automation tools. Conversely there's some potential to output Mermaid code from other analysis tools.

While I don't think diagrams as code will fully replace diagramming tools, I think it shows a lot of potential for capturing architectures on smaller projects.