---
# === IDENTITY ===
id: software/patterns/state-machine-implementation/2026
canonical_question: "How do I implement a state machine in code?"
aliases:
  - "finite state machine implementation pattern"
  - "state machine design pattern TypeScript Python Go Java"
  - "FSM state transition table implementation"
  - "XState statechart implementation"
  - "order lifecycle state machine workflow"
  - "state pattern vs switch case state machine"
entity_type: software_reference
domain: software > patterns > state_machine_implementation
region: global
jurisdiction: global
temporal_scope: 1956-2026

# === VERIFICATION ===
last_verified: 2026-02-24
confidence: 0.92
version: 1.0
first_published: 2026-02-24

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: null
  next_review: 2026-08-23
  change_sensitivity: low

# === CONSTRAINTS ===
constraints:
  - "Every state machine must have exactly one initial state and must define all valid transitions explicitly"
  - "Guard conditions must be pure functions with no side effects; never mutate state inside a guard"
  - "Transition actions (entry/exit/transition) must be idempotent when the state machine is persisted or replayed"
  - "Never allow direct state assignment — all state changes must go through the transition function"
  - "For DB-backed workflows, wrap state transitions and side effects in a single database transaction to prevent inconsistent state"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Need a full workflow engine with parallel branches, compensation, and long-running sagas"
    use_instead: "Workflow orchestration engines (Temporal, Camunda, AWS Step Functions)"
  - condition: "Building a parser or lexer that processes character-by-character input"
    use_instead: "Parser combinators or lexer generators (ANTLR, PEG.js, tree-sitter)"
  - condition: "Need reactive state management for UI components (React, Vue)"
    use_instead: "UI state management (Redux, Zustand, Pinia) or XState for complex UI flows"

# === AGENT HINTS ===
inputs_needed:
  - key: complexity
    question: "How many distinct states does your system have?"
    type: choice
    options: ["3-5 states", "6-15 states", "16+ states or hierarchical"]
  - key: persistence
    question: "Does the state need to survive process restarts?"
    type: choice
    options: ["in-memory only", "database-backed", "event-sourced"]
  - key: language
    question: "What programming language are you using?"
    type: choice
    options: ["TypeScript/JavaScript", "Python", "Go", "Java/Kotlin", "other"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/patterns/state-machine-implementation/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-02-24)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "software/patterns/circuit-breaker/2026"
      label: "Circuit Breaker Pattern"
    - id: "software/patterns/middleware-pipeline/2026"
      label: "Middleware Pipeline Pattern"
    - id: "software/patterns/error-handling-strategies/2026"
      label: "Error Handling Strategies"
  solves: []
  alternative_to: []
  often_confused_with:
    - id: "software/system-design/cqrs-event-sourcing/2026"
      label: "CQRS & Event Sourcing"

# === SOURCES (7 authoritative sources) ===
sources:
  - id: src1
    title: "XState Documentation — State Machines and Statecharts"
    author: Stately
    url: https://stately.ai/docs/machines
    type: official_docs
    published: 2025-12-01
    reliability: high
  - id: src2
    title: "State Design Pattern — Refactoring Guru"
    author: Refactoring Guru
    url: https://refactoring.guru/design-patterns/state
    type: community_resource
    published: 2024-01-15
    reliability: high
  - id: src3
    title: "State — Game Programming Patterns"
    author: Robert Nystrom
    url: https://gameprogrammingpatterns.com/state.html
    type: technical_blog
    published: 2014-11-01
    reliability: high
  - id: src4
    title: "How to Build Type-Safe State Machines in TypeScript"
    author: OneUptime
    url: https://oneuptime.com/blog/post/2026-01-30-typescript-type-safe-state-machines/view
    type: technical_blog
    published: 2026-01-30
    reliability: moderate_high
  - id: src5
    title: "Implementing State Machine Patterns in Go"
    author: Coding Explorations
    url: https://www.codingexplorations.com/blog/state-machine-patterns-in-go
    type: technical_blog
    published: 2024-04-15
    reliability: moderate_high
  - id: src6
    title: "Implementing a State Machine in Java for Backend Systems"
    author: alanrb
    url: https://alanrb.medium.com/implementing-a-state-machine-in-java-for-backend-systems-26bd7e724108
    type: technical_blog
    published: 2025-06-10
    reliability: moderate_high
  - id: src7
    title: "State Machines in Practice: Advantages, Disadvantages, and a TypeScript Example"
    author: d4b.dev
    url: https://www.d4b.dev/blog/2026-01-04-state-machines-advantages-disadvantages
    type: technical_blog
    published: 2026-01-04
    reliability: moderate_high
---

# State Machine Implementation

## TL;DR

- **Bottom line**: Explicit state + transitions prevent invalid states and eliminate boolean flag soup; model your system as states, events, and a transition table.
- **Key tool/command**: `state/event/transition table` — define every valid (state, event) -> nextState mapping; reject everything else.
- **Watch out for**: Using multiple boolean flags instead of a single state enum, which creates impossible/untested state combinations.
- **Works with**: Any language — pattern is universal. XState (TypeScript), python-statemachine (Python), Spring State Machine (Java), or hand-rolled in Go.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Every state machine must have exactly one initial state and must define all valid transitions explicitly
- Guard conditions must be pure functions with no side effects; never mutate state inside a guard
- Transition actions (entry/exit/transition) must be idempotent when the state machine is persisted or replayed
- Never allow direct state assignment — all state changes must go through the transition function
- For DB-backed workflows, wrap state transitions and side effects in a single database transaction

## Quick Reference

| Approach | Complexity | Type Safety | Persistence | Best For |
|---|---|---|---|---|
| Switch/case on enum | Low | Medium | Manual | Simple flows, 3-5 states, no hierarchy |
| State transition table (map) | Low-Medium | High | Easy to serialize | Flat FSMs, config-driven flows |
| State pattern (OOP) | Medium | High | Manual | Open/closed principle, per-state behavior |
| XState / Statecharts | Medium-High | Very High | Built-in (inspect, persist) | Complex UI flows, hierarchical/parallel states |
| DB-backed workflow | High | Medium | Native (row = state) | Order lifecycles, multi-step approval flows |
| Coroutine/generator-based | Medium | Low-Medium | Difficult | Streaming parsers, protocol handlers |

| Component | Role | When to Add |
|---|---|---|
| States (enum) | All valid configurations | Always — define first |
| Events (enum/type) | Triggers that cause transitions | Always — finite set of inputs |
| Transition table | Maps (state, event) -> nextState | Always — the core of the FSM |
| Guards | Boolean predicates that allow/block transitions | When transitions are conditional |
| Entry/exit actions | Side effects on state entry or exit | When states trigger work (API calls, notifications) |
| Context (extended state) | Mutable data carried alongside discrete state | When you need counters, retries, accumulated data |
| Hierarchical states | Nested state machines (substates) | When states share common transitions |
| Parallel states | Orthogonal regions executing simultaneously | When independent concerns evolve independently |

## Decision Tree

```
START
|-- How many states?
|   |-- 2-3 simple states, no guards
|   |   --> switch/case on enum (simplest, inline)
|   |-- 4-10 states, flat transitions
|   |   |-- Need runtime config or serialization?
|   |   |   |-- YES --> State transition table (map/dict)
|   |   |   |-- NO --> State pattern (OOP) or switch/case
|   |-- 10+ states OR hierarchical/parallel
|   |   --> XState / statechart library
|-- Does state survive restarts?
|   |-- YES, short-lived (minutes)
|   |   --> Serialize state to Redis/cache
|   |-- YES, long-lived (hours/days)
|   |   --> DB-backed workflow with row-level state
|   |-- NO
|   |   --> In-memory FSM
|-- Is this a parser or protocol handler?
|   |-- YES --> Coroutine/generator-based FSM
|   |-- NO --> One of the above based on complexity
```

## Step-by-Step Guide

### 1. Define your states as an exhaustive enum

Enumerate every valid state your system can be in. Do not leave room for implicit states. [src3]

```typescript
// TypeScript: use a string literal union for exhaustive checking
type OrderState = 'idle' | 'pending' | 'confirmed' | 'shipped' | 'delivered' | 'cancelled';
```

```python
# Python: use an Enum for type safety
from enum import Enum, auto

class OrderState(Enum):
    IDLE = auto()
    PENDING = auto()
    CONFIRMED = auto()
    SHIPPED = auto()
    DELIVERED = auto()
    CANCELLED = auto()
```

**Verify**: Confirm every state is reachable and every state has at least one exit transition (except terminal states like `delivered`, `cancelled`).

### 2. Define your events

List every trigger that can cause a state change. Events are the inputs to your FSM. [src1]

```typescript
type OrderEvent =
  | { type: 'PLACE_ORDER'; items: string[] }
  | { type: 'CONFIRM_PAYMENT' }
  | { type: 'SHIP'; trackingId: string }
  | { type: 'DELIVER' }
  | { type: 'CANCEL'; reason: string };
```

**Verify**: Every event has at least one state that handles it. Unhandled events should be explicitly rejected, not silently ignored.

### 3. Build the transition table

Map every valid (currentState, event) pair to its nextState. This is the core of your FSM. [src7]

```typescript
const transitions: Record<OrderState, Partial<Record<OrderEvent['type'], OrderState>>> = {
  idle:      { PLACE_ORDER: 'pending' },
  pending:   { CONFIRM_PAYMENT: 'confirmed', CANCEL: 'cancelled' },
  confirmed: { SHIP: 'shipped', CANCEL: 'cancelled' },
  shipped:   { DELIVER: 'delivered' },
  delivered: {},
  cancelled: {},
};
```

**Verify**: Count transitions. For N states and M events, you should have far fewer than N*M entries — most (state, event) pairs are invalid by design.

### 4. Implement the transition function

Write a single function that looks up the transition, applies guards, and returns the new state. [src4]

```typescript
function transition(current: OrderState, event: OrderEvent): OrderState {
  const nextState = transitions[current]?.[event.type];
  if (!nextState) {
    throw new Error(`Invalid transition: ${current} + ${event.type}`);
  }
  return nextState;
}
```

**Verify**: `transition('idle', { type: 'PLACE_ORDER', items: ['A'] })` returns `'pending'`. `transition('delivered', { type: 'CANCEL', reason: 'test' })` throws.

### 5. Add guards and actions

Guards gate transitions; actions execute side effects on entry, exit, or transition. Keep guards pure and actions idempotent. [src1]

```typescript
// Guard: only allow cancellation if not yet shipped
function canCancel(state: OrderState): boolean {
  return state === 'pending' || state === 'confirmed';
}

// Action: send notification on state entry
function onEnterShipped(context: { trackingId: string }): void {
  sendTrackingEmail(context.trackingId); // idempotent
}
```

**Verify**: Test guards return correct boolean for each state. Test actions are idempotent (calling twice produces the same result).

## Code Examples

### TypeScript/XState: Order Lifecycle

> Full script: [typescript-xstate-order-lifecycle.ts](scripts/typescript-xstate-order-lifecycle.ts) (34 lines)

```typescript
// Input:  XState v5 installed (npm install xstate)
// Output: Type-safe order state machine with guards and actions
import { createMachine, createActor } from 'xstate';
const orderMachine = createMachine({
  id: 'order',
# ... (see full script)
```

### Python: Transition Table FSM

> Full script: [python-transition-table-fsm.py](scripts/python-transition-table-fsm.py) (44 lines)

```python
# Input:  Python 3.10+ (match/case syntax)
# Output: Strict FSM that rejects invalid transitions
from enum import Enum, auto
from dataclasses import dataclass, field
from typing import Callable
# ... (see full script)
```

### Go: Interface-Based State Pattern

> Full script: [go-interface-based-state-pattern.go](scripts/go-interface-based-state-pattern.go) (51 lines)

```go
// Input:  Go 1.21+
// Output: State machine using interfaces for open/closed extensibility
package statemachine
import "fmt"
type Event string
# ... (see full script)
```

### Java: DB-Backed Order Lifecycle

> Full script: [java-db-backed-order-lifecycle.java](scripts/java-db-backed-order-lifecycle.java) (34 lines)

```java
// Input:  Java 17+, JDBC or JPA
// Output: Database-persisted state machine with transactional transitions
public enum OrderState {
    IDLE, PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED;
    private static final Map<OrderState, Map<String, OrderState>> TRANSITIONS = Map.of(
# ... (see full script)
```

## Anti-Patterns

### Wrong: Boolean flags instead of explicit states

```typescript
// BAD -- boolean flags create 2^N possible combinations, most of which are invalid
interface Order {
  isPending: boolean;
  isConfirmed: boolean;
  isShipped: boolean;
  isCancelled: boolean;
}

// What does { isPending: true, isShipped: true, isCancelled: true } mean?
// Nothing — but the type system allows it.
```

### Correct: Single state enum

```typescript
// GOOD -- exactly N valid states, no impossible combinations
interface Order {
  state: 'idle' | 'pending' | 'confirmed' | 'shipped' | 'delivered' | 'cancelled';
}

// TypeScript enforces that state is always one of the valid values.
// The compiler catches invalid states at build time.
```

### Wrong: Implicit transitions via direct state mutation

```python
# BAD -- anyone can set any state at any time; no validation
class Order:
    def __init__(self):
        self.state = "idle"

    def ship(self):
        self.state = "shipped"  # No check: can "ship" a cancelled order

order = Order()
order.state = "delivered"  # Direct mutation — bypasses all rules
```

### Correct: Transitions enforced through a single function

```python
# GOOD -- all state changes go through validated transition function
class Order:
    def __init__(self):
        self._state = State.IDLE

    @property
    def state(self) -> State:
        return self._state

    def send(self, event: Event) -> State:
        key = (self._state, event)
        if key not in TRANSITIONS:
            raise ValueError(f"Cannot {event.name} from {self._state.name}")
        self._state = TRANSITIONS[key]
        return self._state
```

### Wrong: Missing guard conditions on transitions

```java
// BAD -- transition always succeeds regardless of business rules
public OrderState ship(Order order) {
    order.setState(OrderState.SHIPPED); // No check: order with 0 items can be "shipped"
    return order.getState();
}
```

### Correct: Guards validate preconditions before transitioning

```java
// GOOD -- guard ensures business invariants before state change
public OrderState ship(Order order) {
    if (order.getItems().isEmpty()) {
        throw new IllegalStateException("Cannot ship an order with no items");
    }
    if (order.getState() != OrderState.CONFIRMED) {
        throw new IllegalStateException("Can only ship confirmed orders");
    }
    order.setState(OrderState.SHIPPED);
    return order.getState();
}
```

## Common Pitfalls

- **State explosion in flat FSMs**: Adding states for every combination (e.g., `pending_with_coupon`, `pending_without_coupon`) leads to exponential growth. Fix: use extended state (context) for data that varies within a state, or hierarchical states for shared transitions. [src1]
- **Forgotten terminal states**: Omitting `type: 'final'` or not detecting completion means actors/processes never clean up. Fix: explicitly mark terminal states and handle the `done` event. [src1]
- **Race conditions in DB-backed FSMs**: Two concurrent requests reading the same row can both transition from the same state. Fix: use `SELECT ... FOR UPDATE` or optimistic locking (`@Version` / `version` column). [src6]
- **Side effects in guards**: Putting API calls or DB writes inside guard functions makes transitions non-deterministic and breaks replay. Fix: guards return `boolean` only; put side effects in entry/exit/transition actions. [src3]
- **No error/failure state**: Happy-path-only FSMs crash ungracefully when something goes wrong. Fix: add an explicit `error` or `failed` state with retry transitions. [src7]
- **Testing only happy paths**: Testing `idle -> pending -> confirmed -> shipped -> delivered` but never testing invalid transitions. Fix: test that every invalid (state, event) pair throws or returns an error. [src4]

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| System has 3+ distinct states with defined transitions | Only 2 states (on/off, enabled/disabled) | Simple boolean or enum toggle |
| Multiple boolean flags create impossible combinations | State changes are trivial and linear (A -> B -> C, no branching) | Simple sequential pipeline |
| Correctness is critical (payments, auth, workflows) | UI component with simple show/hide logic | Conditional rendering / CSS classes |
| You need to persist and resume long-running processes | Real-time streaming data transformation | Stream processors (RxJS, Kafka Streams) |
| Business rules dictate which transitions are allowed | Full workflow with parallel branches, compensation, timers | Workflow engine (Temporal, Camunda) |
| You want to visualize and communicate system behavior | Need probabilistic or fuzzy state transitions | Markov chains, ML models |

## Important Caveats

- XState v5 introduced significant breaking changes from v4 (actors replace services, `createMachine` API changes) — do not mix v4 and v5 patterns in the same codebase. [src1]
- The State design pattern (GoF) and a finite state machine are not the same thing. The State pattern delegates behavior to state objects but does not enforce a transition table — combine both for maximum safety. [src2]
- Statecharts (Harel, 1987) extend FSMs with hierarchy, parallelism, and history — they solve state explosion but add conceptual complexity. Only adopt statecharts when flat FSMs become unwieldy (15+ states). [src3]
- For distributed systems, a state machine running on a single node is not sufficient — consider Raft-based replicated state machines or event-sourced aggregates for consistency across nodes.
- Performance is rarely a concern: even a naive map lookup FSM handles millions of transitions per second. Optimize for correctness and readability first.

## Related Units
<!-- Generated from related_kos frontmatter -->

- [Circuit Breaker Pattern](/software/patterns/circuit-breaker/2026)
- [Middleware Pipeline Pattern](/software/patterns/middleware-pipeline/2026)
- [Error Handling Strategies](/software/patterns/error-handling-strategies/2026)
- [CQRS & Event Sourcing](/software/system-design/cqrs-event-sourcing/2026)
