Mermaid & Copilot: A love/hate journey with diagrams as code

Mermaid & Copilot: A love/hate journey with diagrams as code

Adham 13 min read

I never liked making diagrams.

Not because diagrams are useless. They are extremely useful. But because the process of making them always felt wrong to me. Open a GUI tool, drag boxes around, align arrows pixel by pixel, export as PNG, put it somewhere, and then six months later the diagram is outdated and nobody wants to update it because the original file is on someone’s laptop.

When I first discovered Mermaid, I thought, finally, diagrams that live in text files. I can version control them. I can review them in pull requests. I can write them next to my code.

But then I actually started using Mermaid regularly. And it became a love-hate relationship.

What Mermaid actually is

For those who have not seen it before, Mermaid is a tool that generates diagrams from text. You write something like this:

graph LR
    A[User] --> B[API Gateway]
    B --> C[Service A]
    B --> D[Service B]
    C --> E[Database]

And it renders into a proper diagram with boxes and arrows. No dragging, no clicking, no GUI. Just text.

It supports flowcharts, sequence diagrams, class diagrams, state diagrams, Gantt charts, ER diagrams, and more. The syntax is different for each type, which is one of the things that will annoy you later. But the core idea is always the same: text in, diagram out.

GitHub renders Mermaid natively in markdown files. So if you put a Mermaid code block in your README, in an issue, or in a pull request description, GitHub just shows the diagram. No plugins, no extra steps.

VS Code also has extensions that preview Mermaid live as you type. So you can see the diagram update in real time next to your code.

Why I love Mermaid

Let me start with the good parts, because there are many.

It lives with the code. This is the biggest one for me. When diagrams are text files in the repo, they get the same treatment as code. They go through pull requests. They have history. When someone changes the architecture, they can update the diagram in the same commit. This alone solves the “outdated diagram” problem that every team has.

It is fast for simple diagrams. If I need a quick sequence diagram to explain a flow in a PR description, I can write it in two minutes. No need to open Lucidchart or draw.io or whatever tool the team uses. Just type it right there in the markdown.

It works everywhere GitHub works. README files, issues, pull requests, discussions, wiki pages. Mermaid just renders. This makes it very natural to include diagrams as part of normal development workflow instead of treating them as a separate documentation task.

It is good enough for most cases. I do not need pixel-perfect diagrams for everyday engineering communication. I need something that shows the flow clearly, and Mermaid does that. Architecture overviews, sequence flows, state machines, deployment topologies. For all of these, “good enough” is more than enough.

Why I sometimes hate Mermaid

Now the honest part.

The syntax is inconsistent across diagram types. Flowcharts use one syntax, sequence diagrams use another, and class diagrams use yet another. Every time I switch diagram types, I have to look up the syntax again. It never sticks in my head. The arrow styles are different. The way you define nodes is different. It feels like learning a new mini-language every time.

Layout control is almost non-existent. This is my biggest frustration. Mermaid decides where to put your nodes, and sometimes its decisions are terrible. You have a nice clean flow in your head, and Mermaid renders it with crossing arrows and nodes in weird positions. You can hint at direction with LR or TB, but beyond that, you have very little control.

I have spent more time than I want to admit restructuring my diagram text just to trick the layout engine into doing something reasonable. Adding invisible nodes, reordering definitions, splitting subgraphs. It feels like fighting the tool instead of using it.

Styling is painful. If you want to change colors, fonts, or stroke styles, you enter a world of classDef and style directives that are not intuitive at all. And the syntax for themes and configuration is a separate thing from the diagram syntax. I rarely bother with styling because the effort-to-result ratio is bad.

Complex diagrams become unreadable in source. For a 5-node flowchart, Mermaid source is clean and easy to read. For a 30-node architecture diagram with subgraphs and conditional flows, the source text becomes a wall of characters that nobody wants to review in a PR. At some point, the “diagrams as code” benefit starts working against you because the code itself is hard to understand.

Error messages are not helpful. When your syntax is wrong, Mermaid gives you an error that sometimes points to the wrong line, or just says “parse error” with no useful context. Debugging a broken Mermaid diagram is often just commenting out sections until you find the one bad line.

Where Copilot changes things

Here is where the story gets interesting. GitHub Copilot has changed my relationship with Mermaid completely.

Most of the things I hate about Mermaid are about the friction of writing and debugging the syntax. And that is exactly what Copilot is good at removing.

Generating diagrams from descriptions

Instead of writing Mermaid syntax from scratch, I describe what I want and let Copilot write it. In VS Code chat, in the terminal with the CLI, or even inline in a markdown file.

Something like:

“Draw a sequence diagram showing a user authenticating via OAuth2, where the client redirects to the auth server, the user logs in, the auth server sends a code back, and the client exchanges the code for a token.”

And Copilot gives me a working Mermaid sequence diagram. Not always perfect on the first try, but usually 80-90% there. I then adjust the parts that need fixing, which is much faster than writing from zero.

Here is what the code looks like, and what it renders into:

sequenceDiagram
    participant U as User
    participant C as Client App
    participant A as Auth Server
    participant R as Resource Server

    U->>C: Click "Login"
    C-->>U: 302 redirect to Auth Server /authorize
    U->>A: GET /authorize
    A-->>U: Login and consent page
    U->>A: Submit credentials and grant consent
    A-->>U: 302 redirect to callback with auth code
    U->>C: GET /callback?code=...
    C->>A: POST /token with code (back-channel)
    A-->>C: Access token + refresh token
    C->>R: API request with Bearer token
    R-->>C: Protected resource
    C->>U: Show result
sequenceDiagram
  participant U as User
  participant C as Client App
  participant A as Auth Server
  participant R as Resource Server

  U->>C: Click Login
  C-->>U: 302 redirect to Auth Server /authorize
  U->>A: GET /authorize
  A-->>U: Login and consent page
  U->>A: Submit credentials and grant consent
  A-->>U: 302 redirect to callback with auth code
  U->>C: GET /callback?code=...
  C->>A: POST /token with code (back-channel)
  A-->>C: Access token + refresh token
  C->>R: API request with Bearer token
  R-->>C: Protected resource
  C->>U: Show result
OAuth2 Authorization Code flow, generated from a plain-English description

This matters because the hardest part of Mermaid for me was always remembering the exact syntax for each diagram type. Copilot remembers it for me.

Fixing broken diagrams

When a Mermaid diagram does not render and I cannot figure out why, I paste the code into Copilot and ask “why is this broken?” Nine times out of ten, it spots the issue immediately. A missing bracket, a wrong arrow syntax, a keyword that changed between Mermaid versions.

This saves me from that frustrating cycle of commenting out lines and re-rendering until I find the problem.

Fighting the layout engine

Remember how I said I spend too much time restructuring diagrams to get a reasonable layout? Copilot helps here too. I can describe the layout I want and ask Copilot to restructure the diagram definition to achieve it. It knows the tricks, like reordering node definitions, using invisible links, and splitting subgraphs, that influence the Mermaid layout engine.

I still wish Mermaid had better layout controls, but having Copilot as a layout consultant makes it less painful.

Quick diagrams in PRs and issues

This is probably where I use the combination most. When I am writing a PR description or an issue, and I want to add a quick diagram, I ask Copilot right there in the editor. “Add a flowchart showing the retry logic with exponential backoff.” It writes the Mermaid block, I paste it in, done.

Before Copilot, I would skip the diagram because the effort of writing it was not worth it for a PR description. Now I add diagrams much more often because the cost is so low.

Using the CLI for quick diagrams

With the Copilot CLI, I can generate Mermaid diagrams without even opening an editor. If I am working in the terminal and need a quick diagram, something like:

copilot -p "write a mermaid flowchart showing: user submits form, server validates, if valid save to DB and return success, if invalid return error with details"

I get a Mermaid block back in seconds. I can paste it into a GitHub issue, a documentation file, or wherever I need it.

Practical tips if you want to try this

Here are some things I learned from using Mermaid with Copilot over the past months.

Start with a description, not syntax. Do not try to write Mermaid from memory. Describe what you want to Copilot in plain English. Be specific about the flow, the actors, and the connections. Let it handle the syntax.

Use the right diagram type. Mermaid supports many diagram types, and picking the wrong one makes everything harder. If you are not sure which one to pick, this is how I think about it:

flowchart TD
    A{Events in order\nbetween systems?} -->|Yes| SD[Sequence diagram]
    A -->|No| B{Branching\nlogic?}
    B -->|Yes| FC[Flowchart]
    B -->|No| C{States and\ntransitions?}
    C -->|Yes| ST[State diagram]
    C -->|No| D{Data model\nor classes?}
    D -->|Yes| CD[Class diagram]
    D -->|No| GC[Gantt chart]
flowchart TD
  A{Events in order between systems?} -->|Yes| SD[Sequence diagram]
  A -->|No| B{Branching logic?}
  B -->|Yes| FC[Flowchart]
  B -->|No| C{States and transitions?}
  C -->|Yes| ST[State diagram]
  C -->|No| D{Data model or classes?}
  D -->|Yes| CD[Class diagram]
  D -->|No| GC[Gantt chart]
A simple decision tree for picking the right Mermaid diagram type

Keep diagrams small. If your diagram has more than 15-20 nodes, consider splitting it into multiple smaller diagrams. Each one focused on one aspect of the system. This keeps both the rendered diagram and the source text readable.

Preview as you go. Install a Mermaid preview extension in VS Code. Seeing the diagram render live as you edit makes the iteration much faster than save-render-check cycles.

Do not fight the layout too hard. If Mermaid’s automatic layout is not giving you what you want after a few attempts, it might be a sign that your diagram is too complex, or that you need a different diagram type. Sometimes switching from a flowchart to a sequence diagram, or splitting into two diagrams, gives you a better result than fighting node positioning for an hour.

Version the diagrams with the code they describe. Put Mermaid diagrams in the same repository, ideally in the same directory, as the code they document. When someone changes the code, the diagram is right there asking to be updated too.

Where Mermaid falls short and you need something else

I want to be honest about this too. Mermaid is not the right tool for everything.

If you need pixel-perfect diagrams for a presentation or a published architecture document, use a proper diagramming tool. Mermaid’s output looks fine in a README but it will not win any design awards.

If you need interactive diagrams where people can click on nodes, expand sections, or see additional details, Mermaid is static. Look at D2 or tools that generate interactive SVGs.

If your diagrams are extremely complex with dozens of nodes and many cross-connections, Mermaid’s layout engine will struggle. Tools like Graphviz give you more control over positioning, and dedicated tools like draw.io let you place things manually.

But for the 80% of cases where you need a clear, quick, version-controlled diagram embedded in your development workflow, Mermaid with Copilot is the combination I keep coming back to.

My current workflow

Today, my diagram workflow looks something like this.

For documentation in the repo, I write Mermaid directly in markdown files. Copilot generates the first version, I refine it, and it goes through PR review like any other code change.

For PR descriptions and issues, I ask Copilot to generate a quick Mermaid diagram inline. It takes 30 seconds and makes the PR so much easier to review.

For architecture discussions, I start with a Mermaid diagram to get the idea across quickly. If the diagram needs to be polished for a wider audience, I export it and refine it in a proper tool. But the Mermaid version stays in the repo as the “living” version.

For this blog, I use Mermaid for technical diagrams and request the Diagram Generator to handle the rendering and cleanup. The source stays as text that I can update anytime.

Final thoughts

Mermaid is not perfect. The inconsistent syntax, the limited layout control, and the painful styling will frustrate you. I still get annoyed at least once a week when a diagram renders in a way that makes no sense.

But the core idea, diagrams that live as text in your repository, is powerful. And GitHub Copilot removes most of the friction that made Mermaid annoying to use. When you do not have to remember syntax, debug parse errors, or fight the layout engine by yourself, the balance shifts heavily toward the “love” side.

If you have been avoiding diagrams because the tooling was too heavy, or if you tried Mermaid before and gave up because of the syntax, give it another try with Copilot. The experience is meaningfully different now.

And if you are already a Mermaid user, try letting Copilot handle the parts that annoy you most. You might find yourself adding diagrams in places you never bothered before.

References