Modeling Object Lifecycles with UML State Machines
UML state machine diagrams give object lifecycles formal run-to-completion semantics. Learn the event [guard] / action model, a PlantUML order example, and the mistakes to avoid.
18 Sept 2026, 05:37 UTC

When the hard problem is describing how one object's behavior changes over its lifetime — an order moving from draft to published, a session timing out, a payment being retried — a UML state machine diagram is the most precisely defined tool in the UML toolbox for the job. The StateMachines clause of the OMG UML 2.5.1 specification gives these diagrams formal run-to-completion semantics: an incoming event triggers at most one enabled transition, and that transition completes before the machine considers the next event. That makes a state machine more rigorous than most other UML diagram types for describing behavior, and far more reviewable than prose.
The practical payoff is that business rules move out of comments and into the model. A rule such as "a draft with a zero price cannot be submitted" becomes a guard on a transition — visible in the diagram, diffable in version control, and checkable in review.
How the Semantics Work
A state machine models a single classifier's lifecycle (one Order, one Account) using a small set of elements:
- An initial pseudostate — the solid circle where the lifecycle begins.
- States — the conditions the object rests in between events, such as Draft or UnderReview.
- Transitions — arrows labeled
event [guard] / action. The event triggers the transition, the guard is a boolean condition that must hold for the transition to fire, and the action runs while crossing. - Optionally, a final state (the bullseye marking the end of the lifecycle) and composite states with history pseudostates for resuming substates.
When an event arrives, the machine evaluates the transitions leaving the current state and fires at most one whose guard passes. Within that run-to-completion step the behaviors run in a defined order: exit behaviors of the source state, then the transition's actions, then entry behaviors of the target state. If no transition is enabled, the event is discarded unless the state declares it deferrable — the object stays put.
A Worked Example: an Order Lifecycle in PlantUML
For most teams the right tooling decision is diagrams-as-code: keep the state machine as PlantUML or Mermaid text committed beside the source, so it renders in pull requests and diffs line by line. Here is the order lifecycle in PlantUML syntax:
@startuml
[*] --> Draft
Draft --> UnderReview : submit [price > 0]
UnderReview --> Published : approve
UnderReview --> Draft : reject
Published --> [*]
@enduml
The guard [price > 0] is the point of the exercise: an invalid submission is blocked at the model level rather than in prose. If submit fires while the price is zero, no transition is enabled and the order stays in Draft — the diagram states that explicitly, and a reviewer can check it without reading a separate design document.
Two cautions about this snippet. PlantUML renders diagrams; it does not simulate UML state machine semantics, so a clean render does not prove the machine behaves as intended. And transition-label syntax details evolve between PlantUML releases — validate the event [guard] / action form against the version your team uses before standardizing on it.
Diagrams-as-Code versus Round-Trip Modeling Tools
The tooling decision usually comes down to what the diagram is for:
- Diagrams-as-code (PlantUML, Mermaid): best when the diagram is a reviewed design artifact. It lives in the repository, diffs cleanly, and renders in code review.
- Heavyweight round-trip modeling tools: worth their cost only if the team genuinely profits from generating or synchronizing code from models. Code-generation quality varies widely between tools, so verify current capabilities against the vendor's documentation rather than assuming them.
If the goal is execution rather than documentation, treat the diagram as a specification and implement it with a runtime state-machine library (several exist for Java, C#, and JavaScript, following statechart ideas). Check the library's support for guards, composite states, and history against its current documentation, and verify transition coverage: every state reachable, no dead ends except the final state.
Limits to Respect
- A state machine describes one classifier's lifecycle. It cannot show message flow between objects — use a sequence diagram — or structural relationships — use a class diagram.
- Concurrent concerns require orthogonal regions, which become hard to read quickly; if a machine needs many of them, reconsider the decomposition instead of drawing harder.
- UML itself does not keep the model synchronized with code. Without review discipline, diagrams drift.
Common Mistakes
- Naming states with verbs ("Sending") instead of conditions ("AwaitingApproval"). A verb describes a transition, not a condition the object rests in.
- Omitting guards when two transitions leave the same state on the same event, which leaves the behavior ambiguous.
- Hiding side effects inside guards. Guards should be simple boolean checks; put work in actions or entry and exit behaviors.
- Treating the diagram as a one-off design document rather than a reviewed artifact that changes with the code.
Checking the Result
Render the snippet with a local PlantUML installation or an online PlantUML server and confirm the drawn transitions match the intended rules: one initial transition into Draft, a guarded submit, approve and reject out of UnderReview, and a final state from Published. Then walk the machine through real edge cases — a zero-price submission, a reject arriving after publication — and confirm each is either blocked or intentionally discarded. For the semantics themselves, the StateMachines clause of the OMG UML 2.5.1 specification is the reference for run-to-completion, guard, and entry and exit behavior.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.