Why Diagram Before Coding?

API design mistakes are expensive to fix post-implementation: breaking changes require versioning, database schema changes require migrations, and clients that have integrated against the wrong contract need rework. A diagram session catches these problems at the design stage, where the cost is one conversation instead of three sprints of rework.

The specific diagrams to draw before any API work:

  1. Sequence diagram — the happy path and key error paths for each endpoint
  2. ER diagram — the data model supporting the API
  3. Flow diagram — for async systems, the event flow between services

Sequence Diagrams for REST APIs

A sequence diagram makes the implicit explicit: which services are called, in which order, what can fail, and what happens when it does. Draw this for every non-trivial endpoint before writing code.

sequenceDiagram
    autonumber
    actor Client
    participant GW as API Gateway
    participant OrderSvc as Order Service
    participant InvSvc as Inventory Service
    participant PaySvc as Payment Service
    participant DB as Orders DB

    Client->>GW: POST /api/v1/orders
    Note right of Client: { items: [{id, qty}], paymentToken }

    GW->>GW: validate JWT, extract userId
    GW->>OrderSvc: createOrder(userId, items, paymentToken)

    OrderSvc->>InvSvc: checkAvailability(items)
    alt insufficient stock
        InvSvc-->>OrderSvc: 409 { unavailable: [itemId] }
        OrderSvc-->>GW: 409 Conflict
        GW-->>Client: 409 { error: "OUT_OF_STOCK", items: [...] }
    end
    InvSvc-->>OrderSvc: 200 { reserved: true, reservationId }

    OrderSvc->>PaySvc: charge(amount, paymentToken)
    alt payment declined
        PaySvc-->>OrderSvc: 402 Payment Required
        OrderSvc->>InvSvc: releaseReservation(reservationId)
        OrderSvc-->>GW: 402 Payment Required
        GW-->>Client: 402 { error: "PAYMENT_DECLINED" }
    end
    PaySvc-->>OrderSvc: 200 { chargeId, status: "succeeded" }

    OrderSvc->>DB: INSERT order { items, chargeId, status: "confirmed" }
    DB-->>OrderSvc: orderId

    OrderSvc-->>GW: 201 { orderId, status: "confirmed" }
    GW-->>Client: 201 { orderId, estimatedDelivery }

This diagram immediately surfaces several design questions: Should stock reservation be released asynchronously if payment fails? Does the gateway validate JWT or the service? What's the timeout policy for the inventory check?

Sequence Diagrams for gRPC Services

gRPC adds server-streaming, client-streaming, and bidirectional-streaming patterns that are hard to express in prose. Sequence diagrams make these patterns clear:

sequenceDiagram
    participant C as gRPC Client
    participant S as Streaming Service

    Note over C,S: Server-Side Streaming (live order updates)

    C->>S: WatchOrder(orderId) [unary request]
    activate S
    S-->>C: OrderUpdate { status: "processing" }
    S-->>C: OrderUpdate { status: "shipped", trackingId }
    S-->>C: OrderUpdate { status: "out_for_delivery" }
    S-->>C: OrderUpdate { status: "delivered" }
    S-->>C: EOF (stream closed)
    deactivate S

    Note over C,S: Bidirectional Streaming (live chat)

    C-)S: Message { "Hello" }    [async, no response wait]
    S-)C: Message { "Hi there" }
    C-)S: Message { "How do I reset my password?" }
    S-)C: Message { "Click Settings > Security > Reset" }
    C-)S: CloseStream

ER Diagrams: Design Your Schema Before Writing Migrations

The most expensive mistakes in backend development are schema design mistakes — wrong cardinality, missing foreign keys, ambiguous naming, normalization errors. An ERD session before writing the first migration is an hour that saves days.

Schema design process: (1) Identify the core entities from the API's nouns. (2) Define the relationships and cardinality. (3) Identify the natural keys and which need surrogate UUIDs. (4) Look for missing junction tables. (5) Only then write CREATE TABLE.

erDiagram
    USER {
        uuid id PK
        string email UK "verified, case-insensitive"
        string display_name
        string tier "free|plus|pro"
        string stripe_customer_id UK
        timestamp created_at
        timestamp deleted_at "null = active"
    }

    SUBSCRIPTION {
        uuid id PK
        uuid user_id FK
        string stripe_subscription_id UK
        string plan "plus_monthly|plus_yearly|pro_monthly|pro_yearly"
        string status "active|past_due|cancelled"
        timestamp current_period_end
        timestamp cancelled_at
    }

    TOOL_USAGE {
        uuid id PK
        uuid user_id FK "null = anonymous"
        string tool_slug
        string anonymous_id "hashed IP for anon users"
        timestamp used_at
    }

    SAVED_RESULT {
        uuid id PK
        uuid user_id FK
        string tool_slug
        jsonb input_snapshot
        jsonb result_snapshot
        string label
        timestamp created_at
    }

    USER ||--o{ SUBSCRIPTION : "has"
    USER ||--o{ TOOL_USAGE : "generates"
    USER ||--o{ SAVED_RESULT : "saves"
ERD Review Checklist
  • Every many-to-many relationship has a junction table
  • Foreign keys are indexed (especially join columns)
  • Nullable foreign keys are intentional — document why
  • UUIDs for all PKs (avoids ID enumeration attacks)
  • created_at and updated_at on every table
  • Soft-delete pattern (deleted_at timestamp) for GDPR compliance

Event Flow Diagrams for Async Systems

Event-driven systems are the hardest to reason about from code alone. A flow diagram showing which service emits which event, and which services consume it, makes the system's behaviour visible at a glance.

flowchart LR
    subgraph Producers
        OS[Order Service]
        PS[Payment Service]
        IS[Inventory Service]
    end

    subgraph EventBus["Event Bus (Kafka / SNS+SQS)"]
        E1{{order.created}}
        E2{{payment.succeeded}}
        E3{{payment.failed}}
        E4{{inventory.reserved}}
        E5{{inventory.released}}
    end

    subgraph Consumers
        NS[Notification Service]
        AS[Analytics Service]
        LS[Loyalty Service]
        RS[Report Service]
    end

    OS --> E1
    PS --> E2
    PS --> E3
    IS --> E4
    IS --> E5

    E1 --> NS
    E1 --> AS
    E2 --> NS
    E2 --> LS
    E2 --> AS
    E3 --> NS
    E4 --> RS
    E5 --> RS

This diagram immediately shows: the Notification Service consumes 3 different events — does it deduplicate? The Analytics Service is a consumer of everything — is it a bottleneck? No consumer handles inventory.released except Reporting — is that correct?

API Gateway Architecture Diagrams

For microservices behind an API gateway, a layered flow diagram shows the routing, auth, and rate-limiting concerns separately from the business logic:

flowchart TD
    Client([Client Request])

    subgraph Gateway["API Gateway Layer"]
        WAF[WAF / Rate Limiter]
        Auth[JWT Validator]
        Router[Request Router]
    end

    subgraph Services["Microservices"]
        UserSvc[User Service]
        OrderSvc[Order Service]
        BillingSvc[Billing Service]
        SearchSvc[Search Service]
    end

    subgraph SharedInfra["Shared Infrastructure"]
        Redis[(Redis — Rate Limit Counters)]
        Cognito[Cognito — JWT Validation]
    end

    Client --> WAF
    WAF -->|blocked| Client
    WAF -->|allowed| Auth
    Auth --> Redis
    Auth --> Cognito
    Auth -->|unauthorized| Client
    Auth -->|authorized| Router

    Router -->|/users| UserSvc
    Router -->|/orders| OrderSvc
    Router -->|/billing| BillingSvc
    Router -->|/search| SearchSvc

Embedding Diagrams in API Documentation

The best place for API diagrams is inside the API documentation itself — not in a separate wiki that nobody updates. Practical options:

  • OpenAPI / Swagger docs: Add Mermaid diagrams in the x-mermaid extension or in description fields. Redoc and Stoplight can render them.
  • Readme files: GitHub renders Mermaid natively — add diagrams directly to your README.md or docs/API.md.
  • Docusaurus / MkDocs: Both support Mermaid plugins for auto-rendering in your generated docs site.
  • Confluence: Use the Mermaid for Confluence app (Atlassian Marketplace) to render diagrams inline with API documentation pages.