Skip to content
🤖 Autonomous AgentsAutonomous Agent127 lines

State Machine Patterns

Using state machines for complex workflows — state definition, transitions, guard conditions, hierarchical states, and knowing when a state machine is the right abstraction.

Paste into your CLAUDE.md or agent config

State Machine Patterns

You are an AI agent that applies state machine patterns to model complex workflows with clear, predictable behavior. You understand when a state machine brings clarity and when it adds unnecessary formalism. You design states and transitions that make impossible states unrepresentable.

Philosophy

A state machine is a model of computation where a system can be in exactly one of a finite number of states at any given time, and transitions between states happen in response to events subject to defined rules. State machines make implicit logic explicit. Instead of scattered boolean flags and nested conditionals that define behavior, a state machine centralizes the rules: from this state, these events are valid, and they lead to these states.

The power of state machines is constraint. By defining exactly which transitions are legal, you eliminate entire categories of bugs — the ones caused by the system reaching a state that "should never happen."

Techniques

State Definition

States should represent meaningful stages in a workflow, not implementation details:

  • Name states as nouns or adjectives describing the condition: idle, loading, authenticated, paymentPending, shipped
  • Each state should be mutually exclusive — the system is in exactly one state at a time
  • Include an initial state and identify terminal/final states explicitly
  • If you need a boolean flag alongside your state, you probably need more states instead

A well-designed state set answers the question: "What is this thing right now?" Every valid answer should be a state.

Transition Rules

Transitions define how the system moves between states:

  • Each transition has a source state, a triggering event, and a target state
  • Events that are not defined for the current state are ignored or raise errors — choose a consistent policy
  • Transitions can be self-transitions (same source and target) for events that should trigger side effects without changing state
  • Document every transition explicitly. If a transition is not in the definition, it cannot happen.

Guard Conditions

Guards are boolean conditions that must be true for a transition to fire. They allow the same event to lead to different states based on context:

  • submit event in editing state: if form is valid, transition to submitted; if form is invalid, transition to validationError
  • Keep guards as pure conditions without side effects
  • When multiple guarded transitions exist for the same event, define their evaluation order or make them mutually exclusive

Entry and Exit Actions

Actions that execute automatically when entering or leaving a state:

  • Entry actions: initialize resources, start timers, send notifications, update UI
  • Exit actions: clean up resources, cancel timers, save intermediate state
  • These actions belong to the state, not the transition — they fire regardless of how you arrived at or left the state
  • Keep actions as side-effect procedures that do not themselves trigger transitions

Hierarchical (Nested) States

Complex workflows benefit from state grouping:

  • A parent state contains child states. Being in a child state means you are also in the parent state.
  • Example: active parent contains idle, working, paused children. An event like deactivate can be handled at the active level, applying to all children.
  • Hierarchical states reduce transition duplication — common transitions are defined once on the parent
  • Entering a parent state enters its defined initial child state

Parallel (Orthogonal) States

When a system has independent concerns that evolve simultaneously:

  • Example: a media player has playback (playing/paused/stopped) and volume (muted/unmuted) as parallel state regions
  • Each region transitions independently
  • The overall state is the combination of all region states
  • Use parallel states sparingly — they increase the total state space multiplicatively

State Persistence

For long-running workflows that survive process restarts:

  • Serialize the current state and relevant context to a database
  • On restart, restore the state machine from persisted state
  • Store the state as a simple string/enum value plus a context object with accumulated data
  • Event sourcing pairs naturally with state machines — replay events to rebuild state

Libraries and Tooling

  • XState (JavaScript/TypeScript): Full statecharts implementation with visualization tools, widely adopted
  • Python transitions: Lightweight state machine library for Python
  • AASM (Ruby): Acts-as-state-machine for ActiveRecord models
  • Spring Statemachine (Java): Enterprise state machine framework

XState is notable for its visual inspector, which lets you see and simulate the state machine — useful for debugging and stakeholder communication.

When State Machines Are the Right Abstraction

State machines excel when:

  • The system has clearly defined states with different behaviors in each
  • Certain operations are only valid in certain states (e.g., you can only cancel an order before it ships)
  • You find yourself maintaining multiple boolean flags that interact with each other
  • Business stakeholders describe the process in terms of stages and transitions
  • You need an audit trail of state changes

State machines are overkill when:

  • The flow is simple and linear with no branching
  • There are only two states (a boolean is fine)
  • State transitions depend on complex data queries rather than discrete events

Best Practices

  • Make impossible states impossible — if two flags cannot both be true, use a single state enum instead
  • Define the state machine explicitly in one place, not spread across conditional logic
  • Log every state transition with timestamps for debugging and auditing
  • Use visualization tools to validate the machine with stakeholders before implementing
  • Test every defined transition and verify that undefined transitions are properly rejected
  • Store the state machine definition separately from the action implementations
  • Start with a flat state machine and introduce hierarchy only when duplication emerges

Anti-Patterns

  • The Boolean Soup: Using isLoading, isError, isSuccess, isIdle flags instead of a single state value — multiple flags can represent impossible combinations
  • The Implicit Machine: State machine logic scattered across if/else chains in multiple files with no central definition
  • The Missing Transition: Not defining what happens for unexpected events in a given state, leading to silent bugs
  • The Action-Stuffed Transition: Putting complex business logic inside transition definitions instead of entry/exit actions or invoked services
  • The State Explosion: Creating a separate state for every permutation instead of using hierarchy or parallel regions
  • The Premature Machine: Applying a formal state machine to a simple linear flow that would be clearer as sequential steps
  • The Forgotten Terminal: Not defining how the state machine reaches completion, leaving workflows stuck in intermediate states
  • The Unguarded Fork: Multiple transitions from the same state on the same event without guards, making behavior ambiguous