Sreenidhi
Contact

Designing in States

Designing in code brings bugs you can't draw your way out of — states you never knew a screen could be in, worst on checkout and payments. A state machine maps them all, including the ones you'd miss.

Tool
State machines
Year
2026
Type
Play
Topic
Behavioral spec

The bugs that bite aren’t visual — they’re a user landing in a state I never planned: a payment stuck retrying, a button still spinning after the response came back. So model the screen as a state machine. It’s always in exactly one named state, and only the paths you drew can happen — anything you didn’t, can’t.

Here’s a checkout drawn that way: the happy path as a spine, every edge and failure state hanging off it.

The checkout state machine — a happy-path spine (cart → success) with edge and in-flight states branching off, a coupon sub-flow, and two parallel regions. CHECKOUT MACHINE · 14 STATES · 28 EVENTS Checkout, as a state machine PARALLEL · RUN ALONGSIDE EVERY STATE SESSION active expiring expired NETWORK online offline COUPON · ADDED FEATURE CouponEntering CouponValidating CouponInvalid Cart Reviewing StockCheck PaymentEntry PaymentReady Processing Success Receipt REVIEW_CART PROCEED_TO_PAYMENT STOCK_OK ENTER_PAYMENT SUBMIT_PAYMENT PAYMENT_AUTHORIZED VIEW_RECEIPT APPLY_COUPON COUPON_VALID EDGE & IN-FLIGHT STATES — the ones easy to miss StockProblem ThreeDSecure Declined NetworkError StockProblemDuringPayment SessionLost STOCK_UNAVAILABLE REQUIRES_3DS 3DS_SUCCESS DECLINED TIMEOUT STOCK_UNAVAILABLE SESSION_EXPIRED (from any safe state) user-facing state in-flight · ignores SESSION_EXPIRED terminal parallel region 14 atomic states · 2 parallel regions · 28 events

And each state is a screen. Switch between the base flow, the coupon feature added later, and the states easiest to miss — then drive each one; only the transitions the machine allows light up.

The point isn’t to enforce it — it’s that the diagram and the screens are one thing, and it surfaces the states you’d never have drawn: the failures, timeouts, and half-finished flows where a user gets lost. I wouldn’t reach for this on a settings page; on checkout or payments, where a missed state quietly costs money or trust, it’s worth keeping a living map beside the code.