Key Principle
Knowing statechart notation is necessary but not sufficient. Designers need thinking strategies to move from a blank page to a working statechart. The central cognitive shift: stop asking "what action should this event trigger?" and start asking "in what state is this event meaningful?" Pair this states-first thinking with four structural tests -- completeness, reachability, dead states, determinism -- applied during construction, not after.
Why This Matters
Bottom-up developers instinctively think action-first: "user clicks Delete, so run the delete routine" -- then bolt on context checks ("but only if something is selected... and not in read-only mode..."). Each check is a hidden state distinction. When those distinctions are implicit rather than modeled, they get missed, producing mode errors and unreachable-state bugs. States-first thinking inverts this: enumerate the contexts first, then attach actions to events within each context. The result is a framework where every contextual distinction is explicit and testable, which is why the author claims statecharts reduce UI bugs by 80-90%.
Design tests catch structural flaws while the cost of correction is still low. "It is not intended for the tests to be applied to a statechart when it is completed; they should be applied during the design process to avoid introducing problems" (Chapter 9). A dead state or combinatorial omission discovered late requires reworking everything built on top of it.
Good Examples
States-first thinking: "The designer first identifies the contexts (states) in which the events can occur and then the actions are identified for the events. Identifying the states first builds a solid framework on which events and actions can easily be added" (Chapter 9). Rather than scattering precondition checks across every action handler, preconditions are enforced structurally -- if the system is in state X, only state X's transitions are possible.
Consolidate related / separate unrelated behavior: Items controlled by common events (e.g., cut/copy/paste via menu, toolbar, and keyboard) belong in the same concurrent part. Independent behavior goes in different concurrent parts so that "parts of the design [can] be understood and modified in isolation from the other parts" (Chapter 9). The binding constraint: "A screen item should be controlled by precisely one part of a statechart" (Chapter 9).
No inter-region dependencies: A dependency -- a condition of the form
(in state X)on an event arrow -- negates the value of concurrency. It causes "a sharp increase in the number of test cases" (Chapter 9), hidden side-effects across regions, and comprehension collapse at scale. The fix: merge dependent concurrent parts. The copy-and-paste example shows that merging two 2-state regions produced exactly 4 states and was "considerably easier to understand" (Chapter 9).Actions on states must be unconditional: "Actions on states are like global variables. They can be very powerful, but they also have the potential to cause many problems" (Chapter 9). The design rule: "Use states to control the attributes of user interface items rather than executing actions. All actions should be associated with events" (Chapter 9). States declare what the UI looks like; events describe what happens. This makes adding new transitions safe.
Screen rules as temporary scaffolding: A catalog of every UI item and how its behavior/appearance varies across contexts. The critical question is not "what does it do?" but "when is it available?" Once the statechart is complete, screen rules are discarded -- "Statecharts should be the definitive specification of a user interface's behaviour" (Chapter 9).
Counterpoints
State complexity vs. action complexity: The misconception is that statecharts suit only simple UIs. In reality, "The statechart is only concerned with the complexity of a user interface's states and not the complexity of actions" (Chapter 9). A drag-and-drop interaction may involve complex screen updates, but its state model is just two states. The state model stays simple and auditable even when implementation code is complex.
Route irrelevance: Each state must fully declare its own UI configuration. "If state 1 enables a particular button, then state 2 should not rely on it having been enabled by state 1" (Chapter 9). Path-dependent assumptions create bugs that only manifest under specific navigation sequences, making them nearly impossible to reproduce during testing.
Key Quotes
"It is usually impossible to understand the required behaviour of a user interface and then immediately produce a statechart design for that interface." — Ian Horrocks, Chapter 9
"The point of screen rules is to identify the behaviour of items, not so much in terms of what they will do (that is, the actions), but rather when they are available to a user and what the differences in their behaviour are." — Ian Horrocks, Chapter 9
"Actions on states are like global variables. They can be very powerful, but they also have the potential to cause many problems." — Ian Horrocks, Chapter 9
"If the same event with different conditions leaves a state, then only one of those conditions should evaluate to true at any given time. If this cannot be guaranteed, then the events should be prioritized to avoid non-deterministic behaviour." — Ian Horrocks, Chapter 9
Rules of Thumb
- Think in states first, events second: enumerate the contexts before attaching actions
- Consolidate behavior controlled by common events; separate truly independent behavior
- If concurrent parts reference each other's internal states, merge them
- Keep actions on event transitions, not on state entry/exit, unless the action is truly unconditional for every possible entry path
- Screen rules are a thinking tool, not a specification -- discard them once the statechart exists
- Apply the four design tests continuously during construction, not as a final quality gate
- Completeness: for every state, verify every UI object's events are handled; unhandled events mean a button should be disabled -- "If a button has no effect in a state then it probably needs to be disabled" (Chapter 9)
- Dead states: cross-reference entry actions against exit conditions; a state that disables the only control capable of triggering its exit transition is dead
- Reachability: trace entry paths from the initial state to every state; check default starts for concurrent parts and history mechanisms
- Determinism: verify mutual exclusivity of guard conditions on same-event transitions; check for the same event at multiple hierarchy levels
Related References
core-framework.md— The event-state-action paradigm and states-first thinking that Chapter 9 heuristics operationalizestatechart-notation.md— The notation (hierarchy, concurrency, guards) that the heuristics and design tests assume familiarity withthree-artifact-specification.md— State-item tables and event-action tables used as cross-checking baselines for the completeness test