All articles
Architecture7 min read

State Machines as the UI Layer Source of Truth

Many interface bugs are not rendering bugs. They are state bugs: two controls believe opposite things, a user can trigger an action twice, or an error is shown after the screen has already moved on. State machines make these situations explicit before they reach production.

Booleans multiply faster than intent

A simple form often begins with isLoading, hasError, isComplete, and isOpen. Each flag seems harmless. Together, they allow combinations nobody intended: loading and complete at once, a closed dialog with an active error, or a success message that survives a new request.

The issue is not booleans themselves. The issue is using independent variables to describe a single process. A submission is usually idle, editing, validating, submitting, successful, or failed. Those are mutually exclusive states, and the interface becomes easier to reason about when it says so directly.

Make valid transitions explicit

A state machine describes both the states and the events that move between them. For example, a property enquiry can move from draft to submitted, from submitted to assigned, and from assigned to resolved. It cannot become resolved before it exists, and it should not return to draft without a deliberate event.

Once these transitions live in one place, the UI can derive its available actions from the current state. Buttons stop guessing. Validation has a home. Error recovery becomes part of the model instead of a collection of conditional branches.

Model the workflow, not the screen

A useful machine follows the business process rather than the component tree. The same transition rules should make sense whether the user is on a desktop dashboard, a mobile view, or an automated job. This keeps the product consistent as new interfaces are added.

For small flows, a reducer with a discriminated union is often enough. Larger, long-running workflows may justify a dedicated statechart library. The tool matters less than the discipline of naming states and refusing impossible combinations.

Determinism is a design feature

When a user reports a problem, deterministic state gives the team a path to reproduce it. You can ask which event occurred, what state existed before it, and which transition was allowed. That is far more useful than searching through components for a flag that may have changed in several places.

The payoff is not academic purity. It is a product that behaves predictably under real conditions: slow networks, repeated clicks, partial failures, and people returning to work they started yesterday.

Continue reading