# States and Module Boundaries

Make invalid states harder to express and module behaviour easier to depend on.

## Three flags disagree

A request fails, yet its success badge remains visible. Adding another flag will not resolve the ambiguity. Three independent flags for loading, success and failure permit eight combinations, including combinations your interface should never show.

Figure: One explicit state avoids contradictory combinations of boolean flags. ([s15-booleans](/art/s15-booleans.en.svg))

Every caller inherits the job of interpreting those combinations. Future edits become guesswork for people and agents alike. Define the valid situations before choosing a library.

## Start with a request table

Our example has four states. Predict what should happen if a success event arrives before any request starts. Here, the policy is to ignore that event and retain the current state.

<!-- compare -->
| Current state | Accepted event | Next state |
| --- | --- | --- |
| idle | submit | loading |
| loading | resolve / reject | success / error |
| error | retry | loading |
| success | reset | idle |

Events absent from the table leave state unchanged. A discriminated union can attach results only to success and an error only to error. A reducer centralises event handling; a state machine becomes useful when transitions need more structure.

Figure: Model allowed transitions and test that forbidden transitions are rejected. ([d15-states](/diagrams/d15-states.en.svg))

A simple request may need no dedicated library. You should still be able to name the current state and the events it accepts. A tool name cannot answer those questions for you.

> I think I'm addicted to getting agents to build state machines
>
> — Matt Pocock, [source](https://x.com/mattpocockuk/status/2110818314499072493), 2026-10-15

## What does the interface protect?

A request module should explain its results, failures and dependencies. Tests constrain its implementation. Callers should not need to understand cache keys or retry counters to use it correctly.

Figure: A deep module exposes a simple interface and hides useful complexity. ([s15-deep-module](/art/s15-deep-module.en.svg))

John Ousterhout's deep-module concept concerns a simple interface that hides useful complexity. Neither splitting something into ten files nor squeezing it into ten lines establishes that quality.

## Enforce dependency boundaries

Tools can reject imports that cross an architectural boundary. Types can describe domain failures explicitly. Effect is one of Matt's public preferences and a possible further study topic. This lesson does not cover it or recommend a universal migration.

> **Our inference**
> Our suggestion: choose one boundary with recurring failures. Define its inputs, outputs and failure behaviour, then select a type or check that enforces the agreement.

## Exercise: reject early success

### Produce two kinds of evidence

- Task: specify and enforce what idle does when it receives resolve.
- Invalid path: it must not enter success or invent a result.
- Normal path: submit shows loading; reject shows an error; retry can lead to success.

Suggested approach: The invalid event preserves idle. The normal sequence is idle, loading, error, loading, success. Check both the state and the visible interface.

Done means a test catches early success and a repeatable interaction demonstrates the normal path. Continue with [implementation and feedback](/en/implementation-and-feedback). The archived xstate-catalogue is a historical reference, with no promise of current API compatibility.

## Quick reference

- **State** One tag names the phase; data belongs to that phase.
- **Transitions** List accepted events and a policy for every other event.
- **Interface** Callers depend on behaviour; tests constrain internals.

## Sources

- [Matt on state machines, 2026-10-15](https://x.com/mattpocockuk/status/2110818314499072493)
- [Codebase design article, independently interpreted](https://www.aihero.dev/how-to-make-codebases-ai-agents-love)
- [xstate-catalogue, archived historical examples](https://github.com/mattpocock/xstate-catalogue)

Unofficial, non-commercial study notes. No affiliation with or endorsement by Matt Pocock. Quoted works remain their authors’ copyright; MIT reuse retains its copyright and licence notices.

The diagram CSS on this page is adapted from Archify (https://github.com/tt-a1i/archify) under the MIT licence; the complete licence follows.

### Full licence: Archify (tt-a1i/archify)

```text
MIT License

Copyright (c) 2026 tt-a1i (Archify)
Copyright (c) 2025 Cocoon AI

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
