article

Mermaid Flowcharts and Sequence Diagrams: From Basics to Production

11 min read

In the first part of this series, we covered what Mermaid is and why text-based diagrams belong in your workflow. Now it’s time to get practical. This article walks through the two diagram types you’ll reach for most often: flowcharts and sequence diagrams.

We’ll go from basic linear flows to nested architectural diagrams and full OAuth 2.0 sequences. Each level builds on the previous one, so the examples get progressively more complex.

Flowcharts

Flowcharts are the workhorse of technical documentation. They describe processes, decision trees, system architectures, and just about any “thing A connects to thing B” relationship.

Every flowchart starts with the flowchart keyword and a direction:

Shapes you’ll actually use

ShapeSyntaxTypical use
RectangleA[Text]Action / step
RoundedA(Text)State / event
StadiumA([Text])Start / end
DiamondA{Text}Decision
HexagonA{{Text}}Preparation
ParallelogramA[/Text/]Input / output
CircleA((Text))Connector
Double circleA(((Text)))Event trigger
CylinderA[(Text)]Database / storage

Arrow types

Level 1: A simple linear process

The simplest flowchart is a straight chain of actions. No decisions, no branches. Just steps in order.

```mermaid
flowchart TD
    A([Start]) --> B[Receive order]
    B --> C[Process payment]
    C --> D[Ship product]
    D --> E([End])
```

That’s it. Stadium shapes mark the start and end, rectangles mark the actions. If your process really is this simple, a flowchart is almost overkill, but it still documents the flow in a way that survives team turnover.

Level 2: Decisions and loops

Most real processes have branching logic. Use diamond shapes for decisions and add labeled arrows for each path.

```mermaid
flowchart TD
    A([Start]) --> B[User enters login and password]
    B --> C{Credentials valid?}
    C -->|Yes| D[Grant access]
    C -->|No| E[Show error]
    E --> F{Attempts < 3?}
    F -->|Yes| B
    F -->|No| G[Lock account]
    D --> H([End])
    G --> H
```

The arrow from F back to B creates a loop. Users keep trying until they either succeed or get locked out after 3 failed attempts. This is a common pattern for any retry logic.

Level 3: Subgraphs for logical grouping

Once your diagram has more than 8-10 nodes, it gets hard to read without structure. subgraph lets you group related nodes into named sections.

```mermaid
flowchart TB
    A([Start]) --> B{Order type?}

    B -->|Physical| C[Check warehouse stock]
    B -->|Digital| D[Generate license key]

    subgraph warehouse [Warehouse]
        C --> C1{In stock?}
        C1 -->|Yes| C2[Reserve item]
        C1 -->|No| C3[Order from supplier]
        C3 --> C4[Wait for delivery]
        C4 --> C2
    end

    subgraph digital [Digital Processing]
        D --> D1[Create license]
        D1 --> D2[Send via email]
    end

    C2 --> E[Generate invoice]
    D2 --> E

    E --> F[Send confirmation to customer]
    F --> G([End])
```

The subgraph syntax is subgraph id [Display Label]end. The id is a single word used internally; the label in brackets is what gets displayed. Arrows can cross subgraph boundaries freely.

Level 4: Nested subgraphs for architecture

For larger systems, you’ll want subgraphs inside subgraphs. This works well for showing layered architectures where a backend has multiple services, each with their own internal components.

```mermaid
flowchart TB
    User([User]) --> LB[Load Balancer]

    subgraph frontend [Frontend Layer]
        LB --> Web1[Web Server 1]
        LB --> Web2[Web Server 2]
        LB --> Web3[Web Server 3]
    end

    subgraph backend [Backend Layer]
        subgraph auth [Auth Service]
            Auth[Auth API] --> JWT[JWT Token Service]
            Auth --> OAuth[OAuth Provider]
        end

        subgraph core [Core Service]
            API[REST API] --> BL[Business Logic]
            BL --> Valid[Validator]
            BL --> Transform[Data Transformer]
        end

        subgraph notifications [Notification Service]
            NQ[Notification Queue] --> Email[Email Sender]
            NQ --> SMS[SMS Gateway]
            NQ --> Push[Push Notifications]
        end
    end

    subgraph data [Data Layer]
        DB[(PostgreSQL Primary)]
        DBR[(PostgreSQL Replica)]
        Cache[(Redis Cache)]
        S3[(S3 Object Storage)]
        DB -.-> DBR
    end

    Web1 & Web2 & Web3 --> Auth
    Auth -->|"Token valid"| API

    BL --> DB
    BL --> Cache
    BL --> S3
    BL --> NQ

    API --> DBR
```

A few things to notice here:

Level 5: A CI/CD pipeline

Here’s a realistic CI/CD pipeline showing the full path from a Git push through build, test, and deploy stages with rollback handling.

```mermaid
flowchart LR
    A([Push to Git]) --> B[Lint and Format Check]
    B --> C[Unit Tests]
    C --> D{Tests passed?}

    D -->|No| E[Send report to developer]
    E --> A

    D -->|Yes| F[Build Docker Image]
    F --> G[Push to Registry]

    G --> H{Branch?}

    H -->|develop| I[Deploy to DEV]
    H -->|staging| J[Deploy to STAGING]
    H -->|main| K[Deploy to PRODUCTION]

    I --> L[Smoke Tests DEV]
    J --> M[Integration Tests STAGING]
    K --> N[Health Check PROD]

    L --> O{OK?}
    M --> P{OK?}
    N --> Q{OK?}

    O -->|No| R[Rollback DEV]
    P -->|No| S[Rollback STAGING]
    Q -->|No| T[Rollback PROD]

    O -->|Yes| U([Deployed DEV])
    P -->|Yes| V([Deployed STAGING])
    Q -->|Yes| W([Deployed PROD])

    T --> X[Alert On-Call Team]
```

This example uses the LR direction because pipelines naturally flow left to right. The branching at the H node shows how the same pipeline can deploy to different environments based on the Git branch. Failed health checks trigger rollbacks, and production rollbacks also alert the on-call team.


Sequence Diagrams

Sequence diagrams show interactions between participants over time. They read top-to-bottom, with each horizontal arrow representing a message or API call. When you need to document who calls whom and in what order, this is the right tool.

Core syntax

ElementSyntaxPurpose
Participantparticipant ADeclare a participant
Actoractor AA human participant
Solid arrowA->>B: textSynchronous call
Dashed arrowA-->>B: textResponse / async
Activationactivate A / deactivate AShow active period
Short activationA->>+B: text / B-->>-A: text+ activates, - deactivates
NoteNote over A,B: textComment above participants
Looploop Label ... endRepeated block
Alt/elsealt Cond ... else ... endConditional branching
Optopt Cond ... endOptional block
Par/andpar Label ... and ... endParallel execution
Criticalcritical ... option ... endCritical region
Breakbreak Cond ... endException flow

Level 1: Simple request-response

The most basic sequence: one participant calls another and gets a response.

```mermaid
sequenceDiagram
    actor User as User
    participant Server as Server

    User->>Server: GET /api/products
    Server-->>User: 200 OK (product list)
```

Solid arrows go forward (requests), dashed arrows come back (responses). The as keyword lets you set display names while keeping short IDs for the arrows.

Level 2: Auth flow with conditions

Real-world sequences usually have branching. The alt / else blocks show different paths depending on conditions.

```mermaid
sequenceDiagram
    actor User as User
    participant App as Application
    participant Auth as Auth Service
    participant DB as Database

    User->>App: Enter email and password
    App->>Auth: POST /auth/login
    Auth->>DB: SELECT user WHERE email = ?

    alt User found
        DB-->>Auth: User record
        Auth->>Auth: Verify password hash

        alt Password correct
            Auth-->>App: 200 OK + JWT Token
            App-->>User: Redirect to Dashboard
        else Password incorrect
            Auth-->>App: 401 Unauthorized
            App-->>User: Show error
        end
    else User not found
        DB-->>Auth: null
        Auth-->>App: 404 Not Found
        App-->>User: Show error
    end
```

Notice the nested alt blocks. The outer one checks if the user exists; the inner one checks if the password is correct. Each branch shows a different response path.

Level 3: Microservices with parallel processing

When services run in parallel, use par / and blocks. Combined with activate / deactivate, you get a clear picture of what’s happening concurrently.

```mermaid
sequenceDiagram
    actor Client as Client
    participant GW as API Gateway
    participant OrderSvc as Order Service
    participant PaySvc as Payment Service
    participant InvSvc as Inventory Service
    participant NotifSvc as Notification Service
    participant DB as Database

    Client->>GW: POST /orders (items, address)
    activate GW
    GW->>OrderSvc: Create order
    activate OrderSvc

    OrderSvc->>DB: INSERT order
    DB-->>OrderSvc: order_id 12345

    par Parallel verification
        OrderSvc->>PaySvc: Authorize payment
        activate PaySvc
        PaySvc->>PaySvc: Check limits
        PaySvc-->>OrderSvc: payment_authorized true
        deactivate PaySvc
    and
        OrderSvc->>InvSvc: Check availability
        activate InvSvc
        InvSvc->>DB: SELECT stock WHERE product_id IN (...)
        DB-->>InvSvc: stock_data
        InvSvc-->>OrderSvc: all_available true
        deactivate InvSvc
    end

    alt All confirmed
        OrderSvc->>DB: UPDATE order SET status confirmed

        par Notifications
            OrderSvc->>NotifSvc: Send confirmation
            activate NotifSvc
            NotifSvc-->>Client: Email Order 12345 confirmed
            deactivate NotifSvc
        and
            OrderSvc->>InvSvc: Reserve items
            InvSvc->>DB: UPDATE stock SET reserved true
        end

        OrderSvc-->>GW: 201 Created (order_id 12345)
    else Payment or stock failed
        OrderSvc->>DB: UPDATE order SET status failed
        OrderSvc-->>GW: 400 Bad Request
    end

    deactivate OrderSvc
    GW-->>Client: Response
    deactivate GW
```

This diagram packs a lot in:

Level 4: OAuth 2.0 with PKCE

For the most complex example, here’s a full OAuth 2.0 authorization code flow with PKCE, including MFA handling, token refresh, and error recovery.

```mermaid
sequenceDiagram
    actor User as User
    participant Browser as Browser
    participant App as Client App
    participant AuthServer as Authorization Server
    participant ResServer as Resource Server
    participant TokenStore as Token Storage

    User->>Browser: Click Sign in with Google
    Browser->>App: Initiate OAuth flow

    App->>App: Generate state + code_verifier (PKCE)
    App->>Browser: Redirect to Authorization Server

    Browser->>AuthServer: GET /authorize?response_type=code&client_id=...
    AuthServer->>Browser: Show login form

    User->>Browser: Enter credentials
    Browser->>AuthServer: POST /login (email, password)

    critical Credential verification
        AuthServer->>AuthServer: Validate + MFA check
    option MFA enabled
        AuthServer->>User: Send OTP code
        User->>Browser: Enter OTP
        Browser->>AuthServer: POST /verify-mfa
    option MFA disabled
        AuthServer->>AuthServer: Skip MFA
    end

    AuthServer->>Browser: Redirect callback_url?code=AUTH_CODE&state=...

    Browser->>App: Callback with authorization code
    App->>App: Verify state parameter

    App->>AuthServer: POST /token (code, code_verifier, client_id)
    activate AuthServer
    AuthServer->>AuthServer: Verify code + PKCE challenge
    AuthServer-->>App: access_token + refresh_token + id_token
    deactivate AuthServer

    App->>TokenStore: Store tokens (encrypted)

    loop Every API request
        App->>TokenStore: Get access_token
        TokenStore-->>App: access_token

        App->>ResServer: GET /api/user/profile (Bearer token)
        activate ResServer

        break Token expired (401)
            ResServer-->>App: 401 Unauthorized
            App->>AuthServer: POST /token (refresh_token)
            AuthServer-->>App: new access_token
            App->>TokenStore: Update access_token
            App->>ResServer: Retry request with new token
        end

        ResServer-->>App: 200 OK (user data)
        deactivate ResServer
        App-->>Browser: Display data
    end
```

This one uses every major sequence diagram feature:

It’s a realistic representation of how modern OAuth implementations actually work, including the token refresh mechanism that trips up a lot of developers.

Interactive playground

Want to try editing Mermaid syntax and see the result live? Use the component below to experiment with any of the examples from this article.

Try it:

Practical tips for better diagrams

After building dozens of these, a few patterns consistently produce cleaner results:

What’s next

The final article in this series covers Gantt charts for project planning, along with advanced Mermaid tips like themes, excludes weekends, task statuses, and when to pick each diagram type.