State Machine Transitions: How Guards Choose the Next State
The calculator for this page is switched off while it is checked. The formula is below, and the worked example in the article shows how to use it by hand.
State machines choose their next state by evaluating transitions that are enabled by the current state and an incoming event; a guard is a boolean condition attached to a transition that must evaluate to true for that transition to be taken. When multiple transitions are enabled, the machine resolves the conflict using a priority rule or a deterministic selection algorithm, and if none are enabled, the event is ignored or an explicit “no transition” handler runs.
What a Guard Actually Is
A guard is a predicate—a function that returns true or false—evaluated against the current context (variables, timers, sensor readings, or any other state data). In UML state machine notation, a guard is written in square brackets on the transition label: event [guard] / action. For example, a traffic light controller might have a transition timer_expired [cars_waiting > 0] / change_to_green. The guard cars_waiting > 0 prevents the light from cycling if no cars are present, saving energy. Guards are pure functions: they must not modify state or produce side effects; they only observe and decide.
How the Selection Process Works
When an event occurs, the state machine performs a run-to-completion step. First, it collects all transitions that originate from the current state and are triggered by that event. Then it evaluates each transition’s guard in a defined order. The first transition whose guard evaluates to true is selected. If no guard is true, the event is discarded (or an implicit “ignore” is applied).
The selection order is typically one of:
- Document order: transitions are listed in the source code or model, and the first matching one wins.
- Priority: transitions can be assigned explicit priorities (higher or lower number) to override document order.
- Nested state priority: in hierarchical state machines, transitions from inner states are evaluated before those from outer states (inner-first).
For example, in a vending machine, the “dispense” event might have two transitions from the “ready” state: one with guard inserted_amount >= price and one with guard inserted_amount < price. The machine evaluates the first guard; if true, it dispenses; if false, it evaluates the second and returns change. The order of evaluation ensures exactly one transition fires.
Guard Evaluation and Side Effects
Guards should be side-effect-free. They are evaluated possibly multiple times (e.g., during conflict resolution or in some frameworks during model checking). If a guard mutates a variable, it can lead to non-deterministic behavior. For instance, a guard like [count++ > 5] is a bug because the increment happens during evaluation, not as an action. Instead, the action should be placed on the transition’s effect list: event [count > 5] / count := 0.
Common Mistakes and Misconceptions
A frequent misconception is that guards are evaluated only once per event. In reality, a guard may be evaluated multiple times during a single step if the machine has to check several candidate transitions. Another mistake is assuming that guards can access the event payload without proper syntax; in many frameworks, the event data is passed as a parameter to the guard function. Also, forgetting to handle the “no transition” case can lead to silent event loss, which is often hard to debug. A good practice is to log or explicitly handle ignored events.
How to Choose Guard Conditions
When designing guards, keep them simple and focused on the current context. Use named constants instead of magic numbers. For example, instead of [speed > 50], define MAX_SPEED = 50 and write [speed > MAX_SPEED]. This improves readability and maintainability. Also, consider using guards to represent business rules that are likely to change; putting them in a separate configuration file or rule engine can make the state machine more flexible.
Comparison with Alternatives
Guards are not the only way to control transitions. Some alternatives include:
- Choice pseudostates: In UML, a choice pseudostate evaluates guards after an event has triggered, allowing dynamic branching. Guards are still used, but the branching is explicit.
- Event deferral: Instead of ignoring an event, you can defer it until a later state where a guard might be true. This is different from guards because the event is stored and re-dispatched.
- State patterns in code: In object-oriented programming, you can implement transitions with if-else chains or switch statements. This is less declarative but can be simpler for small machines.
Costs and Performance Considerations
Guard evaluation is generally cheap, but in high-frequency systems (like network protocol handlers), complex guards that involve database lookups or expensive computations can become a bottleneck. To mitigate, cache guard results if the context hasn’t changed, or move heavy checks to actions that run less frequently. Also, be aware that in some state machine libraries, guard evaluation is done for every transition on every event, so O(n) guards are evaluated per event. Keeping the number of transitions per state small helps.
Frequently Asked Questions
What happens if two guards are both true? The machine picks the first one in the evaluation order (usually document order or priority). It does not evaluate both; only one transition fires. To make this explicit, you can assign priorities or reorder transitions.
Can a guard have side effects like incrementing a counter? No. Guards must be pure functions. Side effects should be placed in the transition’s action block. If you need to count occurrences, use an action that increments a variable, and use the guard to read that variable.
How do I debug a transition that never fires? Check the guard logic: print the guard’s inputs and result. Also verify that the event name matches exactly and that the transition is from the correct source state. Many frameworks have trace logging that shows each guard evaluation.
Do guards work in hierarchical state machines? Yes, but the evaluation order matters. Inner states’ transitions are evaluated before outer states’ transitions. If an inner state has a transition with a true guard, it takes precedence over an outer state’s transition for the same event.
Can I use time in a guard? Yes, but you usually need a timer that sets a flag or updates a variable. Guards cannot directly “wait”—they only check current values. For time-based transitions, use a timer event that triggers the transition, and the guard can check the elapsed time if needed.
What is the difference between a guard and a condition in a flowchart? In flowcharts, a decision diamond is evaluated once and branches. In state machines, a guard is evaluated per event and can be re-evaluated each time the event occurs. Also, guards are attached to transitions, not to states, so they are more reusable.
Related
Formula used
in t | Time in state | min | 10 | 0 in speed | Speed | km/h | 60 | 0 in temp | Temperature | °C | 20 | 0 out guard | Guard result | - | if(speed > 50 && temp < 30, 1, 0) | 0
Each line is one input (in, sel, list) or one result (out: label, unit, formula, decimals). How the calculators work
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.