Every legal transition must have a defined outcome
Aliases: undefined transition · total transition function · missing edge
What it is
Behind the interface sits a state machine: saving, saved, failed, queued offline. Every event that can arrive in the current state, from the user or from the system, must have a drawn edge, and the other end of the edge is a legal state. Leave no undefined transition: there is no “this event should not happen now, so we did not write it”. Unwritten edges still arrive in the real world — a double tap, a late network callback, coming back from background, a permission sheet interrupting — and arriving with nowhere to go, the product falls into blank, a dead spinner, or a screen nobody can name.
This leaf is completeness of the edges, not whether the nodes themselves can be assembled into illegal combinations by the data structure. That is another cut.
Why it happens
Completeness of a state machine is a total function: for every (state × event) a next state, even if the next state is “ignore and stay” or “enter an explicit error state”. A partial function looks clean on paper because the author deleted the “should not happen” cells. At runtime those cells fill: the network delivers “success” to an already-cancelled request, the user hits Back during “submitting”, the system rings during “recording”. An undefined cell has no next state; the implementation can only take the language’s default — a null, a leftover frame, or a silently dropped event. After a drop, the state on screen and the real state fork, and every later transition is computed on the wrong node.
“Ignore” is a definition too. Write it down so a test can assert “Back during submit stays on submit and says you cannot leave”, rather than relying on luck. Unwritten ignore and undefined look the same in code; only an incident tells them apart.
Where it stops holding
In theory the event set is infinite (any gesture at any moment). Completeness is against the product’s acknowledged alphabet: submit, cancel, timeout, success, failure, background, resume, permission result. Input outside the alphabet should be swallowed in an outer layer, not leaked into the machine. Hierarchical machines (Harel statecharts) let a parent write a default handler so children need not repeat every line — a way of writing completeness, not a missing edge. Events from an outside system may arrive late: the alphabet must include “late success / late failure”, defined on a state already left as a no-op or an ignorable log, not as a new submit. A happy-path diagram for a demo is not a state machine; using it as the implementation checklist systematically drops edges.
Applying it
- List a table of state × acknowledged event. Every cell names a next state or “ignore (reason)”. Empty cells do not ship.
- Treat late callbacks, double taps, Back, an incoming call, a permission sheet as first-class letters in the alphabet, not as exceptions.
- When undefined appears in logs, fill the edge first, then fix the frame.
- How to check: during “submitting”, separately hit Back, submit again, background, kill the network, let a success callback arrive after cancel. Each should land in a state the table wrote, not a spinner or a blank. Any one that leaves the screen unable to name the current state is a missing edge. Contrast the happy path: “submit → success” passing is not completeness.
Related
- Same group: I3.13.2 If the data model can hold an illegal combination, it will · I3.13.3 Independent booleans mint illegal states that a single enum would forbid · I3.13.4 The UI must be a projection of the state machine, not a visual guess
- Nearby: I3.01 Visibility of system status · I3.09 Optimistic update rollback
- Search terms:
complete transitions·undefined transition·statechart