Finite State machines
When you look at a feature toggle, an order workflow or a retry loop, it’s easy to think you’re just writing a few if statements. But any time you have a handful of distinct situations and rules about how to move between them, you’re really building a state machine. Most of us just don’t call it that. The result is often a tangle of boolean flags and duplicated logic that’s hard to follow and easy to break.
A finite state machine (FSM) offers a simpler way to describe that behaviour. You list the possible states your system can be in, define the events that cause it to move to a new state, and then enforce that it’s always in exactly one state at a time. Because transitions are explicit, you can’t accidentally be both “loading” and “error” at once, and you know exactly what can happen next. That predictability pays off when a user clicks rapidly or an API retries at odd moments.
Why bother?
- Clarity and fewer bugs. State machines make “what can happen next?” explicit. Impossible combinations of flags simply disappear. Logs become a clear timeline of state changes rather than a guesswork of booleans.
-
Easier to evolve. When requirements change, you add or adjust transitions instead of unravelling nested conditionals. State machines scale better than growing lists of
isLoading,hasErrorandisRetryingflags. - Reuse across layers. A single definition of states and transitions can serve both your backend and your UI. The server exposes the current state and allowed events; the client uses that to drive feedback and button enablement. You avoid duplicating business rules in the interface.
- Process friendly. Anything that has a start, middle and end benefits from this model: order processing, approval workflows, file uploads, background job pipelines and feature toggles all map neatly to states and transitions.
A tiny example from my own project
I recently built a simple state manager as an internal service and published it as a small GitHub project. Instead of relying on a heavy framework, this manager stores all transitions in a map keyed by the current state and an event. When an event arrives, it looks up the next state and updates the domain object. Because the transition table never changes, the same manager can be reused across threads without any synchronisation. The approach follows the pattern described by Nicholas Blumhardt’s Stateless library: you supply functions to read and update the state rather than letting the machine manage storage. This lightweight implementation replaced a cluster of if statements in an approval workflow and made it obvious which events were valid at each stage. You can see the full code and an example Spring Boot integration in my GitHub repository; it serves as a working example of a simple finite state machine.
To make this example concrete, the implementation is published as the states repository on GitHub. This open‑source “States Manager” stores its transitions in a map of (state, event) pairs and uses injected functions to read and update the current state. Feel free to clone the project to see how a minimal finite state machine works in practice and adapt it to your own workflow.
Getting started
You don’t need a big framework to benefit from state machines. Start by listing your states and the events that change them. Draw a simple diagram or table. Then write a small function that, given a state and an event, returns the next state. As your needs grow, you can add guard conditions or side effects. If your workflow evolves quickly, there is some up‑front effort in naming states and transitions, but once things stabilise, the clarity and reliability pay for themselves. And when correctness matters—orders, payments, approvals—explicit transitions are a big win.
State machines aren’t esoteric theory; they’re a practical tool for everyday application logic. By making the flow explicit and keeping the transition rules separate from your business code, you get systems that are easier to reason about, test and extend. Next time you reach for another else clause, consider whether a simple state machine might serve you—and your future self—better.
🔗 Related post: Why developers never use state machines