A state machine is a model that describes something as being in exactly one of a limited number of conditions (called “states”) at any given time, with defined rules for moving between those conditions. It’s one of the most fundamental concepts in computer science, but it also shows up everywhere in daily life: a traffic light cycling between green, yellow, and red is a state machine. So is a turnstile that switches between locked and unlocked when you insert a coin or push through.
The concept is powerful because it forces you to define every possible situation a system can be in and every event that can change it. That constraint makes systems predictable, testable, and easier to reason about.
The Core Components
Every state machine is built from the same basic parts. A formal definition uses five elements, but the intuition behind them is straightforward:
- States: The finite set of conditions the system can be in. A door has two: open and closed. A vending machine might have six.
- Inputs: The events or signals that can trigger a change. For a turnstile, the inputs are “coin inserted” and “person pushes.”
- Transitions: The rules that say “if you’re in state A and input X happens, move to state B.” This is the logic of the machine.
- Start state: The state the system begins in. A vending machine starts idle, waiting for coins.
- Final states: One or more states that count as “done” or “accepted.” Not every state machine uses these, but they’re central in theoretical computer science, where the machine is deciding whether to accept or reject an input.
That’s really the whole idea. You define where you can be, what can happen, and what each event does depending on where you currently are. Everything else about state machines is just variations on this structure.
A Vending Machine Example
A classic textbook example walks through a vending machine that accepts coins until it reaches a target amount. The machine starts in an idle state, waiting. Each coin insertion is an input that transitions it to a new state reflecting the running total. Once the total hits the price (say, 75 cents), the machine transitions to a “dispense” state. If the customer overpays, it moves to a “make change” state before returning to idle. A “return” button at any point transitions back to the start.
What makes this useful is that the machine can never be in an undefined situation. Every combination of “current state + input” has a defined outcome. There’s no ambiguity, no forgotten edge case where the machine freezes because a customer inserted a nickel at an unexpected moment. That predictability is exactly why state machines are used so widely in real systems.
Deterministic vs. Nondeterministic
In a deterministic state machine, every state has exactly one possible transition for each input. If you’re in state A and input X arrives, there’s only one place you can go. This is the version that maps most naturally to real software and hardware.
A nondeterministic state machine relaxes that rule. From a given state, the same input could lead to multiple possible next states. The machine effectively explores several paths at once. This sounds impractical (how does a real computer be in two places at once?), but nondeterministic machines are extremely useful as a theoretical tool. They’re simpler to design for complex problems, and there’s a proven mathematical result that any nondeterministic machine can be converted into an equivalent deterministic one. The tradeoff is that the deterministic version may need far more states, sometimes exponentially more.
Moore and Mealy: Two Architectures
When a state machine needs to produce outputs (not just transition between states), there are two classic approaches. In a Moore machine, the output depends only on the current state. If you’re in the “dispensing” state, you dispense, regardless of what input just arrived. In a Mealy machine, the output depends on both the current state and the current input. The same state might produce different outputs depending on what event triggered the transition.
In practice, the distinction matters most in hardware design and embedded systems. Moore machines tend to be simpler and more predictable because their outputs don’t change between clock cycles. Mealy machines can react faster because they respond to inputs immediately, but that responsiveness adds complexity. Both can represent the same behavior; they’re just different ways of organizing the logic.
What State Machines Can and Can’t Do
State machines are computationally limited by design. They have no memory beyond which state they’re currently in. They can’t count to arbitrary numbers, can’t process nested structures of unlimited depth, and can’t handle problems that require remembering an unbounded history of past events. In formal terms, they recognize “regular languages,” which is the simplest class of patterns in the hierarchy of computational complexity.
A Turing machine, the theoretical model for general-purpose computers, is essentially a state machine with an added infinite tape for storage. That tape is the difference between a device that can cycle a traffic light and one that can run a spreadsheet. More powerful models like pushdown automata (state machines with a stack for memory) sit between the two, handling things like matching nested parentheses that a plain state machine cannot.
These limitations aren’t a weakness in practice. They’re what make state machines reliable. When you’re designing a system that should only ever be in a known set of conditions, you don’t want the full power of a general computer. You want something provably correct and easy to verify.
How State Machines Are Implemented in Code
The simplest software implementation is a switch statement or a chain of if/else conditions. A variable tracks the current state, and each branch checks the current state plus the incoming event to decide what to do next. This works fine for small machines with a handful of states, but it gets unwieldy fast. Adding a new state means touching every branch, and the logic scatters across the codebase.
A more structured approach uses a state table: a data structure (often a two-dimensional array or dictionary) where you look up the current state and input to find the next state and any associated action. This separates the machine’s logic from the code that executes it, making changes easier.
For larger applications, the State design pattern creates a separate class for each state, with each class containing the behavior specific to that state. The object delegates its actions to whichever state class is currently active. This keeps state-specific logic self-contained and makes adding new states straightforward without modifying existing code.
Statecharts: Scaling Up
Plain state machines hit a wall when systems get complex. A system with 10 independent yes/no properties technically has 1,024 possible state combinations. Modeling all of them as individual states creates an unmanageable explosion.
David Harel’s statecharts, introduced in 1987, solve this by extending traditional state diagrams with three features: hierarchy, concurrency, and communication. Hierarchy lets you nest states inside other states, so a “logged in” state can contain its own sub-states (browsing, editing, settings) without cluttering the top-level diagram. Concurrency lets independent parts of the system run in parallel within the same chart. Communication lets those parallel regions coordinate when they need to.
The result is compact and expressive. Small diagrams can describe complex behavior, and you can zoom in or out to view the system at different levels of detail. Statecharts are the foundation of UML state diagrams and modern libraries like XState, which bring these ideas into frontend web development for managing complex UI logic in frameworks like React.
Where State Machines Show Up
Real-time embedded systems are the classic home for state machines. Traffic light controllers, elevator logic, medical device interfaces, and industrial automation all rely on them because these systems are “highly state dependent,” meaning the correct action depends entirely on what’s happening right now and what event just occurred. A traffic light doesn’t care about the history of every car that’s passed through. It cares about its current phase and the timer or sensor input that triggers the next phase.
In software, state machines manage network protocol handling (TCP connections move through states like LISTEN, ESTABLISHED, and CLOSING), game character AI (idle, patrolling, chasing, attacking), text parsing and regular expressions, and workflow engines that track documents or orders through approval stages. Frontend developers have increasingly adopted state machines for UI logic because they eliminate entire categories of bugs. When every possible user interaction maps to a defined transition, you can’t accidentally reach an “impossible” screen state.
Compilers use state machines in their first phase to break source code into tokens. Every regular expression engine is, under the hood, a state machine. Even something as simple as a search-and-replace operation relies on one.

