DEV Community

Divyakush Punjabi
Divyakush Punjabi

Posted on

An order isn't a row — it's a state machine

Most bugs in commerce aren't math bugs — they're state bugs. An order gets refunded after it shipped. A "delivered" order goes back to "preparing." A cancel button works on an order that's already out for delivery. Every one of those is the same mistake: treating an order like a row you edit, instead of a machine that moves through states. Building Saturdays hammered that lesson in.

An order is a lifecycle, not a record

A row in a table can hold any value you write to it. That's the problem. Nothing about a plain status column stops you from setting a delivered order back to pending, or refunding an order that never got paid. The database will happily let you write nonsense, because a column doesn't know that some transitions are legal and most aren't.

An order has a life: placed → accepted → preparing → out for delivery → delivered, with cancelled and refunded branching off at specific points and nowhere else. That shape is the real business logic. If it lives only in your head, every developer who touches the code re-derives it — and gets it slightly wrong.

Make the illegal states unreachable

The fix is to stop asking "what's the new status?" and start asking "is this transition allowed from where we are?" You model the order as a finite state machine: each state declares the moves it permits, and the code refuses everything else by default.

  • Guarded transitions. You can only move out_for_delivery → delivered, never delivered → preparing. The rule lives in one place, not scattered across every handler that happens to touch an order.
  • Side effects hang off transitions, not fields. "Send the receipt" fires on the transition into delivered, exactly once — not every time some code re-saves the row. This is how you stop double-notifications and phantom refunds.
  • The impossible becomes unrepresentable. A cancel request on a delivered order isn't a bug you patch later; it's a move the machine simply doesn't offer.

The takeaway

The instinct is to model the world as data you mutate. But anything with a lifecycle — an order, a subscription, a support ticket, a deploy — is better modeled as a state machine, because the value isn't the current state, it's the set of legal moves between states. Encode that, and a whole category of "how did the data get into this shape?" bugs stops being possible.

Saturdays taught me to look for the state machine hiding inside every workflow. The full build — the checkout, the payment path, the order flow — is on the project page.

👉 See it: www.divyakush.com/projects/saturdays


Divyakush Punjabi — Full-Stack & AI Systems Engineer

🌐 https://www.divyakush.com · 💼 LinkedIn · 💻 GitHub

Top comments (0)