Skip to content
Folio

Diagrams

SequenceFigure

A call chain over time: who asks whom, in what order, and what comes back.

Usage

When to reach for it

The only figure whose vertical axis means something. A message carries an explicit y, so two calls eight units apart happened together and two two hundred apart did not.

Ships from@misoto22/design/diagrams

TSX
import { SequenceFigure } from '@misoto22/design/diagrams'

Notes

A call chain over time: who asks whom, in what order, and what comes back.

The one diagram type here whose vertical axis MEANS something. Every other figure's y is layout; a sequence's y is time, which is why a message carries an explicit y rather than an index — two calls 8 units apart happened together, and two 200 apart did not, and an evenly spaced list of messages would erase that distinction while looking tidier.

Lifelines are hairlines, not solid rules, because they are the axis rather than the content. A lifeline drawn at the weight of a message is a diagram where seven vertical lines compete with twelve horizontal ones.

Activation bars say who is BUSY, which is the fact a sequence diagram carries that a list of calls does not — the third participant's bar overlapping the second's is the reason to draw them at all.

Segments band the axis into phases — request, fallback, response — printed as a rule with a mono caption rather than as a tinted panel, for the same reason architecture boundaries are: a second ground inside the figure would sit under every message label's mask.

A return message is dashed AND takes an open arrowhead. Two signals rather than one, because the reply is the thing a reader most often needs to pick out of a dense trace, and a dash alone is doing the same work as dashed already does for an asynchronous call.

Anatomy

SequenceFigure anatomy
ElementDescription
Figure shellrequiredThe frame around the picture. What a trace hands it is a message list, and it is published in the order the messages array is written — which is the only ordering a reader who cannot see the axis is given.
Participant headrequiredA 148-unit plate per participant at the top of its column, in the order the participants array puts them. meta.column_fit: "spread" widens every column to the widest name instead of holding the fixed width.
LifelinerequiredA hairline dropped from each head to below the last thing on the axis. A hairline and not a rule, because it is the axis: seven lifelines at message weight is seven vertical lines competing with twelve horizontal ones.
MessagerequiredA horizontal line at its own y, inset from both lifelines so the arrowhead lands clear of the axis, with the wording on a mask above it. A return is dashed AND takes an open head — two signals, because the reply is what a reader picks out of a dense trace.
Activation barA narrow bar on a lifeline from one y to another: who is BUSY. It is the fact a sequence diagram carries that a list of calls does not, and one bar overlapping another is the reason to draw the picture at all.
Segment bandA dashed rule across the whole figure with a mono caption on a masked patch — request, fallback, response. A caption rather than a tinted panel, because a second ground inside the figure would sit under every message label’s mask.

Best practices

Do

  • Author the messages in ascending y. The picture reads the axis and the summary list beside it iterates the array, so a message written out of order is drawn at one point in the exchange and read aloud at another.
  • Reach for meta.column_fit: "spread" when a participant’s name is longer than the fixed column. The plate wraps to two lines and ellipsises whatever still does not fit, and shortening the name to make it fit is not a repair — the name is the data.
  • Space the messages by what actually happened. Nothing normalises y, so eight units between two calls reads as together and two hundred reads as a wait; an evenly spaced list is tidier and says something the trace does not.

Don’t

  • A message naming a participant that participants does not declare leaves the picture and the summary list together — there is no column to draw it between, and a text equivalent reporting a call the picture cannot show is two halves of one figure disagreeing. Development prints DIAGRAM_EDGE_DANGLING with both ends, which is now the only place that message is reported at all.
  • This figure ASSERTS order. Three calls a service makes concurrently, given three y values because they had to be given something, are a picture claiming the second waited for the first — an activation bar or a segment caption is where "these overlap" belongs.

Examples

default

A cache miss read down the page, where the vertical axis is time rather than layout: the 50 units between the miss and the query are a real gap, and an evenly spaced list of six calls would have erased it while looking tidier. The activation bars carry the fact a list of calls cannot — Redis is busy for 45 units inside the 210 the API spends open. Returns are dashed and take an open head, two signals rather than one, because the reply is what a reader picks out of a dense trace first.

A cache miss

The vertical axis is time, not layout.
A cache miss4 elements and 6 relationships. The vertical axis is time, not layout.CLIENTWeb appReactSERVICEAPIhandlerDATARedisDATAPostgres

4 elements and 6 relationships. The vertical axis is time, not layout.

  • Web app (frontend) — React
  • API (backend) — handler
  • Redis (database)
  • Postgres (database)
  • Web appAPI: GET /me
  • APIRedis: read
  • RedisAPI: miss
  • APIPostgres: select
  • PostgresAPI: rows
  • APIWeb app: 200 JSON
  • Phase Request covers GET /me, read, miss.
  • Phase Fallback covers select, rows, 200 JSON.
  • API is busy from GET /me to 200 JSON.
  • Redis is busy from read to miss.
  • Postgres is busy from select to rows.
Key
  • Primary path
  • Call
  • Return

an asynchronous hop

The asynchronous half of a delivery. The enqueue is dashed because Billing does not wait for it — Billing has no activation bar at all, which is what says the two attempts that follow cost the caller nothing. Both calls out carry the security variant, which the key reads as crossing a trust boundary, and it is true here: the endpoint belongs to the customer. column_fit is spread because Partner webhook endpoint does not fit the fixed 148-unit column, and shortening the name to make it fit would be a different diagram.

Webhook delivery

A queue absorbs the retry the caller never waits for.
Webhook delivery3 elements and 5 relationships. A queue absorbs the retry the caller never waits for.SERVICEBillinginvoice writerQUEUEOutboxdelivery queueEXTERNALPartner webhook endpointcustomer-operated

3 elements and 5 relationships. A queue absorbs the retry the caller never waits for.

  • Billing (backend) — invoice writer
  • Outbox (messagebus) — delivery queue
  • Partner webhook endpoint (external) — customer-operated
  • BillingOutbox: enqueue
  • OutboxPartner webhook endpoint: POST /hooks
  • Partner webhook endpointOutbox: 503
  • OutboxPartner webhook endpoint: attempt 2
  • Partner webhook endpointOutbox: 200
  • Phase Retry covers attempt 2, 200.
  • Outbox is busy from enqueue to 200.
Key
  • Asynchronous
  • Crosses a trust boundary
  • Return

time as the axis

What an explicit y buys. The first three calls sit 12 units apart because they left the gateway together, and the vendor reply is 186 units below its call because that is how long it took — placed from an index instead, the six messages would space evenly and that difference would be gone. The activation bars carry the same fact a second way: two close early while the third runs to the bottom of the figure, which is the tail, visible before anyone reads a label.

A slow dependency

Three calls in 24 units, then 186 of waiting.
A slow dependency4 elements and 6 relationships. Three calls in 24 units, then 186 of waiting.SERVICEGatewaySERVICESearchSERVICERankerEXTERNALPricing vendor

4 elements and 6 relationships. Three calls in 24 units, then 186 of waiting.

  • Gateway (backend)
  • Search (backend)
  • Ranker (backend)
  • Pricing vendor (external)
  • GatewaySearch: query
  • GatewayRanker: features
  • GatewayPricing vendor: quote
  • SearchGateway: hits
  • RankerGateway: scores
  • Pricing vendorGateway: price
  • Gateway is busy from query to price.
  • Search is busy from features to hits.
  • Ranker is busy from quote to scores.
  • Pricing vendor is busy from hits to price.
Key
  • Call
  • Primary path
  • Return
The tail
  • Search and Ranker both answer within 75 units of the first call.
  • The vendor holds its request open for 186.

names that do not fit

column_fit spread, which widens every column to the widest participant name instead of holding the fixed one. Reach for it the moment a real name would be clipped: the plate wraps to two lines and ellipsises the rest, and shortening the name to make it fit is not a repair — the name is the data, and dealer-portal-api is a different service from the api. A message naming a participant that participants does not declare is not drawn at all, because there is no column to draw it between; it stays in the summary list as the raw id, so the text reports a call the picture does not show.

Signing in to the dealer portal

Every column as wide as the longest real name.
Signing in to the dealer portal3 elements and 4 relationships. Every column as wide as the longest real name.CLIENTDealer portal (browser)Next.jsSERVICEdealer-portal-apiDjango + DRFDATASQL Server 2025legacy schema

3 elements and 4 relationships. Every column as wide as the longest real name.

  • Dealer portal (browser) (frontend) — Next.js
  • dealer-portal-api (backend) — Django + DRF
  • SQL Server 2025 (database) — legacy schema
  • Dealer portal (browser)dealer-portal-api: POST /api/v1/token/
  • dealer-portal-apiSQL Server 2025: SELECT dealer WHERE email = ?
  • SQL Server 2025dealer-portal-api: one row
  • dealer-portal-apiDealer portal (browser): access + refresh
  • dealer-portal-api is busy from SELECT dealer WHERE email = ? to access + refresh.
  • SQL Server 2025 is busy from SELECT dealer WHERE email = ? to one row.
Key
  • Primary path
  • Call
  • Return

Accessibility

  • Return messages are dashed AND take an open arrowhead — two signals, because the reply is what a reader most often needs to pick out of a dense trace.
  • The message list beside the picture reads in order, which is the same order the axis is drawn in.