State Diagrams
State diagrams show how a machine transitions between states. They're ideal for technical documentation and understanding machine logic.
What State Diagrams Show
- States as boxes or circles
- Transitions as arrows between states
- Events as labels on transitions
- Guards as conditions on transitions
Basic Example
From this EventFlow:
flow
machine: @order
scenario: order lifecycle
on> :checkout from @customer
order moves to #awaiting_payment
on :payment_success from @payment
order moves to #paid
on :payment_failed from @payment
order moves to #payment_failed
on :ship from @warehouse
order moves to #shippedGenerates:
@order
┌───────────────────────────────────────────────┐
│ │
│ ┌─────────┐ │
│ │ (start) │ │
│ └────┬────┘ │
│ │ │
│ │ :checkout │
│ ▼ │
│ ┌────────────────────┐ │
│ │ #awaiting_payment │ │
│ └────────┬───────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ │ │
│ :payment_ :payment_ │
│ success failed │
│ │ │ │
│ ▼ ▼ │
│ ┌───────┐ ┌────────────────┐ │
│ │ #paid │ │ #payment_failed│ │
│ └───┬───┘ └────────────────┘ │
│ │ │
│ │ :ship │
│ ▼ │
│ ┌──────────┐ │
│ │ #shipped │ │
│ └──────────┘ │
│ │
└───────────────────────────────────────────────┘
─── = transition
▼ = directionState Types
Initial State
The first state when a machine instance is created:
○ (initial)
│
│ :create
▼
┌─────────┐
│ #draft │
└─────────┘Final States
States with no outgoing transitions:
┌───────────┐
│ #completed│ ← Final state
└───────────┘
┌───────────┐
│ #cancelled│ ← Final state
└───────────┘Intermediate States
States with both incoming and outgoing transitions:
│
▼
┌─────────┐
│#pending │ ← Intermediate
└────┬────┘
│
▼Transitions with Guards
When transitions have conditions:
flow
on :approve from @manager
? amount < 1000
order moves to #approved
? amount >= 1000
order moves to #needs_director_approval ┌───────────┐
│ #pending │
└─────┬─────┘
│
┌─────┴──────┐
│ │
[<1000] [>=1000]
│ │
▼ ▼
┌─────────┐ ┌──────────────────────┐
│#approved│ │#needs_director_approval│
└─────────┘ └──────────────────────┘Self-Transitions
When an event keeps the machine in the same state:
flow
on :retry from @system
? attempts < 3
$attempts increases by 1
// stays in #processing ┌─────────────┐
│ #processing │◀─┐
└─────────────┘ │
│ │
└── :retry ┘
[attempts < 3]Complex State Machines
Order Lifecycle
@order
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌──────┐ :add_item ┌─────────┐ :checkout ┌─────────┐ │
│ │#empty│──────────────>│ #cart │────────────>│#checkout│ │
│ └──────┘ └────┬────┘ └────┬────┘ │
│ │ │ │
│ :clear :submit │
│ │ │ │
│ ▼ ▼ │
│ ┌──────┐ ┌──────────┐ │
│ │#empty│ │#submitted│ │
│ └──────┘ └────┬─────┘ │
│ │ │
│ ┌─────────────────────┤ │
│ │ │ │
│ :approve :reject │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │#approved │ │#rejected │ │
│ └────┬─────┘ └──────────┘ │
│ │ │
│ :ship │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │#shipped │ │
│ └────┬─────┘ │
│ │ │
│ :deliver │
│ │ │
│ ▼ │
│ ┌───────────┐ │
│ │#delivered │ │
│ └───────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘Branching and Merging
Branching
┌─────────┐
│#pending │
└────┬────┘
│
┌─────┼─────┐
│ │ │
:approve :reject :cancel
│ │ │
▼ ▼ ▼Merging
┌──────────┐ ┌──────────┐
│#approved │ │#fast_track│
└────┬─────┘ └─────┬────┘
│ │
└──────┬───────┘
│
:process
│
▼
┌──────────┐
│#completed│
└──────────┘Reading State Diagrams
Follow the Flow
- Start at the initial state (○ or the first state)
- Follow arrows labeled with events
- Note guards in brackets [condition]
- End at final states (states with no outgoing arrows)
Identify Paths
- Happy path: The expected successful flow
- Error paths: Flows that lead to error/failure states
- Recovery paths: Flows from error states back to normal flow
Use Cases
Technical Documentation
Include state diagrams in technical docs:
bash
eventflow diagram order.flow --type=state --format=svgCode Reviews
Visualize changes to state machine logic:
bash
# Compare diagrams before/after changes
eventflow diagram order.flow --type=state > after.svg
git show HEAD~1:order.flow | eventflow diagram --type=state > before.svgDebugging
Trace through states to understand behavior:
bash
eventflow diagram order.flow --type=state --highlight-path="checkout,payment_success,ship"Best Practices
Keep States Focused
Each state should represent a clear, distinct condition:
flow
// Good - clear states
#draft
#pending_review
#approved
#rejected
// Avoid - vague states
#state1
#processing
#doneName Transitions Clearly
Events on transitions should explain what triggers the change:
flow
// Good - clear transitions
:submit_for_review
:approve_order
:reject_with_reason
// Avoid - unclear
:next
:do
:okDocument Terminal States
Make it clear which states are final:
┌───────────────┐
│ #completed │ ← Success final state
│ (final) │
└───────────────┘
┌───────────────┐
│ #cancelled │ ← Failure final state
│ (final) │
└───────────────┘