State Machine Workflow
Model a business process as a set of defined states with explicit transitions between them. Unlike linear workflows, items can move forwards, backwards, and loop — matching how real business processes actually behave.
On this page
Visual Flow
Rendering diagram…
When to Use This Pattern
Use a state machine when:
- The process is non-linear — items can go backwards, loop, or skip steps
- A simple "start to finish" workflow doesn't capture the real process (e.g., drafts can be rejected and revised multiple times)
- You need to enforce valid transitions — not every state can lead to every other state
- The item has a lifecycle with clear phases (Draft → Review → Approved → Published → Archived)
How It Works
Rendering diagram…
Implementation Guide
Step 1: Define States and Transitions
Map every possible state and every valid transition:
| Current State | Action | Next State | Who Can Act | Side Effects |
|---|---|---|---|---|
| Draft | Submit | In Review | Author | Notify reviewer |
| In Review | Approve | Approved | Reviewer | Notify author |
| In Review | Reject | Draft | Reviewer | Notify author with feedback |
| In Review | Request Changes | Draft | Reviewer | Notify author with specific items |
| Approved | Publish | Published | Editor | Publish to website |
| Approved | Revoke | Draft | Manager | Notify all parties |
| Published | Archive | Archived | Admin | Remove from website |
| Published | Unpublish | Approved | Editor | Remove from website, keep approved |
| Any | Cancel | Cancelled | Author + Manager | Notify all parties |
Step 2: Implement the State Engine
The core loop:
WHILE state != terminal_state:
valid_actions = get_valid_transitions(current_state, current_user_role)
display_task(item, valid_actions)
chosen_action = wait_for_user_response()
IF chosen_action IN valid_actions:
execute_side_effects(current_state, chosen_action)
current_state = transition_table[current_state][chosen_action]
log_transition(old_state, chosen_action, new_state, user, timestamp)
ELSE:
reject_invalid_action()
Step 3: Store the Transition Table Externally
Don't hardcode transitions in the workflow. Store them in a list or database:
| From State | Action | To State | Allowed Roles | Enabled |
|---|---|---|---|---|
| draft | submit | in-review | author | Yes |
| in-review | approve | approved | reviewer,manager | Yes |
| in-review | reject | draft | reviewer,manager | Yes |
This lets business users modify the process without touching the workflow.
Step 4: Build the Task Form Dynamically
Show only the valid actions for the current state:
- Query the transition table for the current state
- Filter by the current user's role
- Display only those action buttons
- Include required fields for each action (e.g., "rejection reason" only shows when rejecting)
Step 5: Visualize the State History
Show the item's complete journey:
| Timestamp | From | To | Action | By | Comment |
|---|---|---|---|---|---|
| Mar 15, 10:00 | — | Draft | Created | Jane | Initial draft |
| Mar 15, 14:30 | Draft | In Review | Submit | Jane | Ready for review |
| Mar 16, 09:15 | In Review | Draft | Reject | Bob | Needs budget section |
| Mar 16, 16:00 | Draft | In Review | Submit | Jane | Budget section added |
| Mar 17, 11:00 | In Review | Approved | Approve | Bob | Looks good |
Tips & Best Practices
Avoid "god states" — a state that can transition to any other state. This defeats the purpose of having a state machine. If you need a "reset" action, create it as a specific transition from specific states.
- Terminal states are final. Once an item reaches Archived, Cancelled, or Completed, no transitions should be possible. This provides data integrity.
- Guard conditions. Some transitions might need additional conditions beyond role: "Can only publish if review score > 80%". Implement these as pre-transition checks.
- Timeout transitions. Combine with Escalation with SLA Timeout — if an item sits in "In Review" for 5 days, auto-transition to "Escalated".
- Version the state model. When you change the states/transitions, items already in-flight should continue with the old model. New submittals use the new model.
Related patterns
Saga with Compensating Transactions
Coordinate a multi-step business transaction that spans several systems by pairing each step with a rollback action. If a later step fails, run the rollbacks in reverse to restore a consistent state.
Fan-Out / Fan-In
Split a workload into parallel branches, process them simultaneously, then aggregate the results. Dramatically reduces processing time for batch operations.
Round-Robin Task Assignment
Distribute incoming work evenly across a team by rotating through assignees in order. Simple, predictable, fair — and exactly right when skill is uniform and load balance matters.