Beginner14 min readUML & Diagramminglive prototype

State Diagrams

A state diagram shows every situation one object can be in over its life, and which events move it from one to the next. It is the picture that spells out the legal lifecycle — and catches the illegal jumps.

The idea

What it is

A state diagram (also called a state machine or statechart) is a picture of the different situations one object can be in over its lifetime — and the events that move it from one situation to the next. Each situation is a state; each move is a transition. That's the whole idea: states are the dots, events are the arrows between them.

Think of a traffic light. It is always in exactly one state — Red, Green, or Yellow — and it can only move along set paths: Red → Green → Yellow → Red. It can never jump straight from Red to Yellow. Or think of an online order: it goes Placed → Paid → Shipped → Delivered. You cannot ship an order that hasn't been paid for. A state diagram draws exactly those rules.

The one sentence to remember

A state diagram = states (the situations an object can be in) joined by transitions (the events that move it between them). It shows the legal lifecycle — so any move that isn't drawn is a move that isn't allowed.

Mechanics

How it works

The pieces of a state diagram

A state diagram is built from just a handful of symbols. Once you know these five, you can read any state diagram. Here is an order's life drawn out:

Placed Paid Shipped Delivered ◉ final state pay() ship() deliver() Cancelled ◉ final state cancel() cancel()
Each box is a state; each arrow is a transition triggered by an event like pay(). ● is where life starts and ◉ marks a final state (Delivered, Cancelled) — and notice there's no arrow for an illegal move, so you can't ship() an order that isn't Paid yet.

State — a rounded rectangle

A state is a situation the object is currently in. It is drawn as a rounded rectangle with a name inside, like Paid. At any moment the object is in exactly one state. The name describes a condition, not an action — Shipped, not ship.

Initial state — a filled black circle

The initial state is a small filled black circle ● with an arrow pointing into the first real state. It marks where the object's life begins. An order's life begins at Placed. There is exactly one initial marker per diagram.

Transition — a labelled arrow

A transition is an arrow from one state to another, labelled with what causes the move. The full label reads event [guard] / action:

  • event — the trigger that fires the move, e.g. pay(). This is the part you almost always see.
  • [guard] — an optional condition in square brackets that must be true for the move to happen, e.g. pay() [funds ok]. If the guard is false, the transition does not fire.
  • / action — an optional thing the object does as it moves, e.g. pay() / sendReceipt.

How to read one transition

Read an arrow as “when event happens, and [guard] is true, move to the next state and do action.” So Placed ──pay() [funds ok] / chargeCard──▶ Paid means: “in Placed, when pay() happens and funds are ok, charge the card and move to Paid.”

Final state — a filled circle with a ring

The final state is a filled circle inside a ring ◉ (a 'bullseye'). It means the object has reached the end of its life — there are no transitions out of it. An order is finished once it is Delivered or Cancelled. A diagram can have several final states.

Illegal events are simply ignored

This is the most important idea. If an event happens and there is no transition drawn for it from the current state, that event is illegal — it is ignored, or it raises an error. You cannot ship an order that is still Placed, because no ship() arrow leaves Placed. The diagram is a contract: only the drawn moves are allowed.

The whole point: prevent invalid jumps

A state diagram captures the legal lifecycle. Its real value is showing the moves that aren't allowed. If ship() doesn't have an arrow out of Placed, then shipping an unpaid order is impossible — the diagram caught the bug before any code was written.

Two finishing touches

  • Self-transition — an arrow that leaves a state and loops back to the same state, e.g. a connection that handles a retry() without changing state. It re-runs the entry/exit work but stays put.
  • Entry / exit actions — work done every time you enter or leave a state, written inside the box as entry / lockSeat or exit / unlockSeat. Handy when many transitions share the same setup or cleanup.
  • Composite (nested) state — a state that contains its own little state diagram inside it. Shipped might hold sub-states InTransit and OutForDelivery. Use it to hide detail until you need it.

Interactive prototype

See it. Build it. Break it.

A sandboxed, hands-on simulation — no setup, no install. Play with it as you read.

About this simulation

A clean state diagram for an Order: Placed → Paid → Shipped → Delivered, with Cancelled off to the side. The current state glows. Press an event button — pay, ship, deliver, cancel — and watch what happens. A legal event slides the highlight to the next state and the panel explains the move; an illegal event refuses to move and tells you exactly why ("you can't ship an order that isn't paid yet"). One fixed note panel, replaced each click, so nothing scrolls away.

Hands-on

Try these yourself

Open the prototype above, predict what happens, then verify.

try 01

Drive a legal path

Open the prototype. The current state is Placed. Click pay, then ship, then deliver — one at a time. Watch the highlight walk Placed → Paid → Shipped → Delivered and read each note as it appears. You just drove the object along its legal lifecycle, one transition at a time.

try 02

Try an illegal jump

Hit Reset to go back to Placed. Now click ship first. The highlight does NOT move — the panel tells you why: there is no ship() transition out of Placed, so the event is blocked. This is exactly what a state diagram protects you from: shipping an order that was never paid for.

try 03

Reach a final state, then poke it

Drive the order all the way to Delivered (or click cancel from any non-final state to reach Cancelled). Now try clicking any event. Nothing moves — a final state ◉ has no transitions out of it. The object's life is over. Notice how the reachable buttons shrink as you progress: that is the lifecycle narrowing.

In practice

When to use it — and what trips people up

When to reach for a state diagram

Draw one whenever an object has a clear lifecycle with rules — a set of stages it moves through, where some moves are allowed and others are not. The moment you hear yourself saying “you can't do X until Y has happened,” you have a state machine on your hands and a state diagram will make those rules obvious.

Good candidates

Orders (Placed → Paid → Shipped → Delivered), payments (Pending → Authorized → Captured / Failed), documents (Draft → Review → Published), connection/session status (Connecting → Open → Closed), UI workflows (wizard steps), and game entities (Idle → Walking → Jumping). State diagrams are brilliant for spotting illegal transitions early, before they become bugs.

What it gives you

  • Makes the legal lifecycle explicit — everyone can see at a glance which moves are allowed and which aren't.
  • Catches invalid transitions early (shipping an unpaid order) before they sneak into code as bugs.
  • Maps almost one-to-one to a clean implementation: one state enum plus one guarded method per event.
  • Far clearer than a tangle of if/boolean flags for describing 'what state is this thing in?'

Common mistakes

  • Only models one object's lifecycle at a time — it doesn't show how objects collaborate (that's a sequence diagram).
  • Blows up fast: many states × many events can produce a dense, hard-to-read diagram (composite states help).
  • Easy to forget transitions, leaving gaps where an event silently does nothing.
  • Overkill for objects that don't really have stages — not every class is a state machine.

Reference

Code & further reading

A minimal reference implementation and pointers worth bookmarking.

// Each diagram arrow becomes a guarded method. The state is the single source of truth.
type State = "Placed" | "Paid" | "Shipped" | "Delivered" | "Cancelled";

class Order {
  private state: State = "Placed"; // ● initial state → Placed

  // Placed ──pay()──▶ Paid
  pay(): void {
    if (this.state !== "Placed") throw new Error("can only pay a Placed order");
    this.state = "Paid";
  }

  // Paid ──ship()──▶ Shipped   (no ship() arrow leaves Placed → illegal)
  ship(): void {
    if (this.state !== "Paid") throw new Error("can only ship a Paid order");
    this.state = "Shipped";
  }

  // Shipped ──deliver()──▶ Delivered ◉
  deliver(): void {
    if (this.state !== "Shipped") throw new Error("can only deliver a Shipped order");
    this.state = "Delivered";
  }

  // Placed/Paid ──cancel()──▶ Cancelled ◉   (final states can't be cancelled)
  cancel(): void {
    if (this.state === "Delivered" || this.state === "Cancelled")
      throw new Error("order already finished");
    this.state = "Cancelled";
  }

  current(): State { return this.state; }
}

References & further reading

5 sources

Knowledge check

Did it land?

Quick questions, answers revealed on submit. Sign in to save your best score.

question 01 / 05

In a state diagram, what does a single rounded rectangle represent?

question 02 / 05

An order is in the Placed state. There is no ship() transition drawn out of Placed. What does this tell you?

question 03 / 05

On a transition labelled pay() [funds ok] / chargeCard, what is the role of [funds ok]?

question 04 / 05

What does a filled circle inside a ring ◉ mean in a state diagram?

question 05 / 05

Which question is a state diagram best at answering?

0/5 answered