CorriDraw CorriDraw
Tutorial

Sequence Diagrams 101: How Systems Talk to Each Other

A beginner's guide to UML sequence diagrams. Learn participants, lifelines, activation bars, and the four message flavours (sync, async, return, self). Master alt/opt/loop fragments, then walk through a complete password-reset flow that spans a browser, an API, a database, and an email service.

PV

Palakorn V.

Product Lead

15 min read
Sequence Diagrams 101: How Systems Talk to Each Other

What Is a Sequence Diagram?

A sequence diagram is a picture of a conversation. Specifically, the conversation between the parts of a running system — a browser talking to a server, a server talking to a database, a mobile app talking to an auth service, or any other dance of back-and-forth messages that makes a feature work. Where a class diagram shows the shape of the code, a sequence diagram shows the choreography: who talks to whom, in what order, with what message.

Here is a small one. Four participants, six messages, and the complete story of how a login request travels from a user's fingers to their freshly-loaded dashboard.

User Browser API Server Database enter email & password POST /login SELECT * FROM users user record 200 OK + token show dashboard
A sequence diagram of a login. Participants across the top, time flowing down, messages as arrows crossing the lifelines. Dashed arrows mark replies heading back.

That picture tells a complete story in seconds: the user types credentials, the browser posts them, the server queries the database, the database returns the row, the server returns a token, and the browser shows a dashboard. Every arrow is one message — one HTTP request, one function call, one user interaction. This article teaches you to draw diagrams like this one from scratch, in plain UML.

Why Sequence Diagrams Are Worth Your Time

Sequence diagrams are the single best tool for thinking about asynchronous and multi-component flows. When a feature is touched by a frontend, a backend, a database, and a third-party service, drawing a sequence diagram forces you to answer:

  • Who calls whom, and in what order?
  • What happens if this call fails? Does the caller wait, retry, or give up?
  • Which calls are synchronous (the caller waits) and which are fire-and-forget?
  • Where are the slow parts of the flow? Which dependencies are on the critical path?

These questions are hard to answer from code alone because the relevant lines of code live in four different files in three different repositories. A sequence diagram pulls them onto one page, in the correct order, and makes the whole dance visible.

The three situations where a sequence diagram earns its keep:

  • Designing a new feature that touches multiple services. Draw the diagram before writing code. You'll surface edge cases that would otherwise land as bugs.
  • Debugging a cross-system issue. "Why is this user seeing a 500?" Walk through the sequence diagram with the actual user's ID; the broken step stands out.
  • Onboarding someone to a complex flow. "Go read the auth code" is a bad instruction. "Look at this sequence diagram first, then the code will make sense" is a good one.

Participants and Lifelines: The Vertical Lines

Every sequence diagram starts with the cast. Each participant — a user, a system, a component, a service — gets a rectangle at the top of the diagram, labelled with the participant's name. Directly below the rectangle, a dashed vertical line called the lifeline drops down to the bottom of the diagram. That lifeline represents the participant's existence through time; anything that happens to the participant shows up as a mark on its lifeline.

Browser API Server Participant the name of an actor, service, or object Lifeline time flows downward along this Activation bar the thin rectangle marks when the participant is actively doing work
The three parts of the "who" in a sequence diagram: a participant box, the lifeline hanging below it, and an optional activation bar that marks the intervals when the participant is working.

A note on the activation bar. It's the thin white rectangle that sits on top of the lifeline during the time a participant is actively doing something. Activation bars are optional — simple diagrams skip them — but they're very helpful for complex flows because they show what is blocking on what. Long activation bar = long-running call; no bar = participant is idle.

Time in a sequence diagram always flows downward. The first message is at the top; later messages are lower. Don't put a later message higher than an earlier one — every reader on earth assumes top-means-first.

Messages: The Horizontal Arrows

An arrow from one lifeline to another is a message. The sender is at the tail; the receiver is at the head. The label above the arrow describes what's being sent — a method call, an HTTP request, a user action, whatever fits.

UML distinguishes four message flavours, each with its own arrow style:

Synchronous message caller waits for a reply. Solid line, filled triangle. getUser(id) Asynchronous message fire-and-forget. Solid line, open arrow. sendEvent(payload) Reply (return) the answer to a prior sync call. Dashed line, open arrow. User record Self-message a participant calls its own method. Loop on the lifeline. validate() Which one should you use? • HTTP request with a response the caller waits for → synchronous. • Publishing to a queue, emitting an event, sending a webhook → asynchronous. • The reply to a synchronous call → dashed reply arrow (pointing back at the caller). • A participant calls its own helper method → self-message.
The four message flavours. Match the arrow style to the semantics — synchronous vs. async vs. return is a real architectural distinction.

A few practical tips on messages:

  • Label every message. Just the method name is fine for code-level diagrams; a short phrase works for higher-level ones ("fetch prices", "persist order"). Unlabeled arrows give a reader nothing to hold onto.
  • Return arrows are optional for obvious returns. If a method is clearly synchronous and the reply carries no information ("save()" → "ok"), most authors skip the return arrow. Include it when the reply carries data the reader cares about.
  • Keep arrows horizontal. A diagonal arrow visually suggests that time is passing during the transfer. For network calls that's technically true, but it's rarely what the diagram is about — horizontal is clearer.

Combined Fragments: If, Loop, and Parallel

Real flows have branches. "Try the cached answer; if that fails, go to the database." "Retry three times before giving up." "Fire off three requests in parallel and wait for all of them." UML expresses branches and loops with combined fragments — a labelled rectangle drawn around the messages the branch applies to. The rectangle has a little tag in the top-left corner that names the kind of fragment.

The five you'll actually use:

  • alt — alternative. "If X, do these messages; else, do those." The box is split by a horizontal dashed line into two (or more) compartments, one per branch, with the guard condition at the top of each.
  • opt — optional. "If X, do these messages; otherwise, skip." A single-branch version of alt.
  • loop — repeat. "Do these messages while X is true" or "do these messages N times". The guard goes in square brackets after the label.
  • par — parallel. "These branches happen concurrently." Split by a dashed line like alt, but the branches run at the same time rather than alternating.
  • break — early exit. Rarely used; means "if X, do these messages and then exit the enclosing fragment".
API Server Cache Database alt [cache hit] get(key) value [cache miss] get(key) null SELECT row row
An alt fragment. The cache is checked first; on a hit, we're done. On a miss, we fall through to the database. Both branches live inside one labelled rectangle.

Combined fragments compose — you can put a loop inside an alt inside another loop. Don't go crazy; more than two levels of nesting is a sign the diagram is trying to do too much, and you should split it into two diagrams instead.

Worked Example: A Password Reset Flow

Let's put every piece together. Password reset is a rich example because it involves a user, a frontend, a backend, a database, and a third-party email service — and the flow touches two different sessions (the request and the click from the email).

The steps:

  1. User clicks "Forgot password" and enters their email.
  2. Frontend sends a reset request to the API.
  3. API looks up the user. If the email exists, it generates a reset token and stores it. Either way, it returns a generic "check your email" response (so attackers can't probe for valid emails).
  4. If the user existed, API fires off an email asynchronously to the email service.
  5. User clicks the link in the email. Frontend sends the token plus a new password to the API.
  6. API verifies the token against the database, updates the password, and returns success.
User Browser API Database Email Svc enter email POST /forgot opt [user exists] find user by email user row store reset token send reset email (async) 200 "check your email" show confirmation click link, enter new password POST /reset (token, pwd) verify token + update pwd ok 200 OK
The full password-reset flow. Five participants, a mix of synchronous and asynchronous calls, one opt fragment, and both halves of the user's journey — the request and the click on the email.

A careful reader will notice a few design details on display:

  • The "send reset email" call is asynchronous. The API doesn't wait for the email service to finish before responding to the browser — if it did, a slow email provider would slow down every password-reset request.
  • The response is the same ("check your email") whether the user existed or not. That's the opt fragment: the database and email work only happen if the user exists, but the outer response is identical.
  • The diagram shows two separate user interactions — the original "forgot password" click at the top and the "click the link in the email" later. They're the same user but effectively different sessions.

You could hand this picture to a backend engineer and they could implement the flow from it alone. That's what a good sequence diagram does.

Common Beginner Mistakes

Five mistakes show up in almost every first sequence diagram.

  • Messages out of time order. Time flows down. If message B happens after A in the real flow, it must be drawn lower than A on the page. Sounds obvious; beginners still get it wrong every time they shuffle the diagram's layout.
  • Missing participants. The classic version: "OK who sent this message?" and the sender is someone you forgot to put at the top. Every sender and receiver of any message needs to be in the participant row.
  • Confusing sync with async. If your diagram shows a message and then a return arrow, that's synchronous — the caller waited. If there's no return, that might be async (fire-and-forget) or it might be a missing return arrow. Pick the right style deliberately.
  • Too much detail in one diagram. A sequence diagram with 30 messages across 8 participants is unreadable. Split it into sub-diagrams per logical phase — "authentication", "authorization", "fetch data", "render" — and let the reader dive into the one they care about.
  • Forgetting to label fragments. An alt without guard conditions is just a rectangle; the reader has no idea which branch represents which case. Always label the [guard] at the top of each fragment compartment.

Where to Go Next

Sequence diagrams pair naturally with other UML diagrams. Three good next steps:

  • Class diagrams. Every participant in a sequence diagram is usually an instance of a class from a class diagram. Build both in tandem: the class diagram says what can exist, the sequence diagram says what they do together.
  • Activity diagrams. When the branch logic inside a single participant gets complex, an activity diagram (basically a flowchart with extra UML primitives) handles it better than stuffing combined fragments into a sequence diagram.
  • Observability in production. Once you have a sequence diagram of a flow, the distributed traces you see in tools like Jaeger or Datadog start to make immediate sense — each span is one message in the diagram, with a measured duration.

Sequence diagrams also map very cleanly to text-based formats. Mermaid's sequenceDiagram syntax is widely supported and lets you write the diagram in plain text inside a design doc, then render it visually with any compatible viewer.

A Note on Tools

Everything above is about the notation — the shapes, the arrow styles, the layout rules — and it applies anywhere you can draw. Whiteboard, notebook, Figma, Visio, Google Drawings, dedicated UML tools: a clean sequence diagram is a clean sequence diagram. If you want a drawing tool with pre-built lifeline shapes, arrow-snapping that keeps messages horizontal, and a Mermaid importer for flipping between text and pictures, CorriDraw — the tool you're reading this on — is one option. But the skill is transferable. You now know how to read and draw a sequence diagram in any tool you pick up.

Share this story
It’s
Free!
Forever
Let’s draw together

Ready to start collaborating ?

Join thousands of teams using CorriDraw for their visual collaboration needs.