← Back to Stateful
PRACTICE.md ↗
Stateful Practice

Practice

A practical guide to running Stateful on your project. Written for whoever you are on the team: designer, engineer, PM, or a hybrid. No visual-design background assumed.

The output is a state map: an explicit understanding of every condition your system can be in, scoped, with the in-scope active states' re-entry behaviour written down. Do it on a whiteboard, or hand the legwork to an agent.

State
A condition the system occupies. Form unsaved, dose overdue, tab closed mid-flow.
State space
The set of conditions a system can be in. Effectively infinite for non-trivial systems.
State list
The output of step 2: every state, across four categories, before scope decisions.
State map
Your artefact naming the subset of the state space relevant to current work: the state list plus scope tags and re-entry specifications.
Scope
Each state is in, out-with-implication, or out. The middle category captures states you're not designing this iteration but that constrain in-scope work through copy, naming, or data model.

Scope

The method runs on a system sentence, and a sentence works best where states are dense. That altitude is a feature or a surface, not a whole product. A sentence for an entire app ("a bookstore-and-reader app is…") abstracts so hard it generates nothing. The two worked examples in the source repository (password reset, medication tracker) are at feature scale, not app scale, and that's deliberate.

For a whole product, the practical shape is two things:

  • One system sentence per feature or surface. Sign-in, browse, checkout, reader, library, settings each get their own sentence and their own state map. Run the four steps once per sentence.
  • A short cross-cutting inventory. States that recur across features (unauthenticated, offline, subscription expired, sync conflict, low storage, first-run) are listed once for the whole product. Each feature's triage references the cross-cutting list rather than re-listing.

The cross-cutting inventory is a shared vocabulary the feature-level maps inherit from, not a separate map with its own re-entry specifications. If a cross-cutting condition is in scope for a feature, its re-entry specification lives with the feature that renders it.

Step 01

Describe

Write one sentence about what the system is as an object that exists over time, not what it does for the user.

A feature-framed sentence (a meeting invite helps people schedule a time to meet) generates no states because helps schedule is an activity, not a condition. A structural sentence generates states from its verbs and time-bearing nouns.

Test for done Three to five distinct states fall out of the nouns and verbs. Then read the sentence to a colleague unfamiliar with the product; if they can name three distinct conditions without prompting, you're done.

Raw material If a draft stalls, answer five questions about the system before rewriting: what exists when nobody is looking, and what windows or deadlines govern it (time); what gets created and what can happen to it before it ends (lifecycle); whether the process leaves the product and comes back (channels); what may have changed between start and finish (completion differences); where it shows itself beyond the primary screen (surfaces). The answers are ingredients for the sentence, not the sentence. On an existing codebase the same questions are answered from structural evidence: schedulers and expiries, status enums, outbound senders, session handling, routes and templates.

Common slips Feature-framing leaks in (helps the user). Flow-framing leaks in (first X, then Y). The sentence reads well but yields nothing.

When an agent drafts the sentence it presents the sentence with a line or two on why it works, and waits for you to confirm or adjust before any states are listed or written down. The sentence anchors everything downstream; it is yours to sign off, same as triage.

STRUCTURAL SENTENCE A meeting invite is a time-bound proposal that gathers responses and resolves into a confirmed event. Underline verbs and time-bearing nouns. states fall out 01 sent 02 partial 03 confirmed 04 cancelled 05 expired 3–5 states
Bold-marked verbs and time-bearing nouns. Three to five states fall out.
Step 02

List

List states across four categories. Each category has its own line of questioning.

Active

Conditions during normal operation. Derived from the sentence's verbs and time-bearing nouns. Expect 8–15.

Failure

Design work is a rendered rejection at the moment of action: link expired, password fails policy, server error.

Interruption

Design work is a re-entry specification on return: tab closed, app backgrounded, time passed, device switched.

Surface

States of the system presenting itself on a surface: email HTML, watch complication, screen reader, push notification.

ACTIVE FAILURE INTERRUPTION SURFACE 8–15 8–15 8–15 6–15 Filled. Verbs & time. Hollow. Could prevent next? Dashed. User leaves, time passes. Squares. Surfaces.
Each category has its own line of questioning, and its own visual identity in your map.

Coverage check Before you stop, run the list against the sentence: every structural term (each underlined verb and time-bearing noun) generates at least one active state; every active state has an explicit answer, or an explicit none, to the failure question and the interruption questions; every surface you listed maps to at least one surface state. A term that generates nothing is either decorative — rewrite the sentence — or hiding states you haven't found yet.

Patterns for finding states

When a category yields a thin list, the following elicitation patterns help. Use them in this order on new work, in any order on existing work.

  1. 01

    Underline the verbs

    From the system sentence, underline verbs and time-bearing nouns. Each verb has a before, during, just-after. Each time-bearing noun has within, before, after.

    → Active states.
  2. 02

    What could prevent the next transition?

    For each active state, push past generic failures (validation error) to second-order ones (link already used, password matches old).

    → Failure states.
  3. 03

    What if the user leaves?

    For each active state, what happens if the user closes the tab, backgrounds the app, drops the network?

    → Interruption states.
  4. 04

    What if time passes?

    What's true of this state fifteen minutes from now? Two hours? A day? A week?

    → Interruption states (and sometimes failure states).
  5. 05

    Surface sweep

    List every surface the system reaches the user on. For each, identify which active states render on it.

    → Surface states.

For harder cases: incident archaeology, multi-actor read, UI inversion, the "and then what?" drill.

Step 03

Decide

Triage each state into one of three scope tags. Engineering and product input is high-value here.

in

Designed in this iteration.

out-with-implication

Not designed now, but has consequences for in-scope work. Annotate each with a one-line note describing the implication.

out

Not designed, no current implication. Documented so it's not rediscovered later.

STATE LIST in DESIGNED NOW out-with- implication CARRIES NOTES out DOCUMENTED ONLY
Every state lands in one bucket. The middle tier carries notes; the right tier just sits documented.

Test for done Every state has a tag. No state is unmarked.

Triage is your call, even when a tool drafts it. An agent recommends a scope per state with a one-line rationale and then stops; it doesn't commit scope autonomously, and it doesn't move on to step 4 until you have triaged. Automation proposes, you dispose.

Step 04

Specify

For each in-scope active state, write specifications for behaviour under re-entry: the state entered from a context other than the canonical one.

  1. Q1

    What does the user see if they return after time has passed?

  2. Q2

    What does the user see if they arrive from a notification, deep link, or different device?

  3. Q3

    What does the user see if they arrive without the preceding state having been shown?

  4. Q4

    What does the user see if the underlying data has changed since they last saw this state?

  5. Q5

    What does the user see if they arrive during or after a failure in the system?

STATE IN-SCOPE · ACTIVE time passed Q1 deep link Q2 Q3 no prior state Q4 data changed prior failure Q5 Re-entry conditions compose.
Each applicable question gets a 2–4 sentence spec. Read them together.

Re-entry conditions compose. A state re-entered after time has passed and on a different device and after a data change is one specification that must remain coherent under all three, not three independent ones.

Common slips Treating re-entry as a single "returning user" variant. Treating it as copy work. Omitting Q4 (data changed) and Q5 (prior failure) — the most commonly skipped and the most production-incident-producing.

After the map

You now have a state map: every state, scoped, with re-entry specifications on the in-scope active ones. What you do with it is your choice.

  • Reorganise the design file. Frame per in-scope state, named after the state. Surface variants nested inside each state. Re-entry variants adjacent.
  • Update a state-map.json mirror. Optional. For teams that want machine validation, codegen, or a contract with downstream tooling.
  • Write it down somewhere. A document, a tracking tool, a brief — whatever fits the team.

The method produces understanding. What you deliver from that understanding is a separate decision.

STATEFUL.md

STATEFUL.md is a small markdown file the skills (and any practitioner) produce and update in the working directory. It's the persistent working memory of the method on a project.

# Stateful working memory

Last updated: <date>

## System sentence
<one sentence, structural>

## Surface(s) in scope
- `<path>`: <one-line description>

## State list
<summary of category counts>

## Triage
In: <n> · Out-with-implication: <n> · Out: <n>

## Re-entry specs
- <state-id>: <count> applicable variants

Optional. Its value compounds across sessions: the second time you run a skill in the same project, the agent has context.

Optional dimensions

Two annotations that pay off on cross-functional projects and existing-product audits.

Layers

Which layers must change to implement or remediate this state?

  • fe Frontend. Surface, presentation, client-side state.
  • be Backend. Service, business logic, computation.
  • dm Data-model. Schema, persistence, history.
  • inf Infrastructure. OS, push delivery, network, device.

Cause (failure states)

The source of a rejection. Independent of how the failure renders.

  • user-input User-input. What the user provided. Coach to a valid configuration.
  • system System. Internal error or bug. We're on it, plus retry.
  • external External. Dependency failed. Name the dependency.
  • policy Policy. Deliberate business rule. Explain without apologising.
Worked example

See the method applied

A medication tracker walked through all four steps, with layers and cause annotated. 52 states, 45 in-scope.