fivenines
7/39
Lesson 1.4

The order state machine

What you'll learn

Every state an order can occupy, every legal transition — and why enumerating them kills a whole class of bugs.

Building on

1.2 showed one happy path; 1.3 showed partial fills. This lesson closes the map: all the paths.

Distributed-systems bugs love undefined states: "the cancel arrived while the fill was in flight — now what?" The exchange's answer is to make the order's lifecycle an explicit state machine, and to let the matching engine alone perform transitions. Everyone else — gateways, risk, the trader — merely requests transitions and learns the verdict from execution reports.

stateDiagram-v2
    [*] --> Pending: received by core
    Pending --> Rejected: fails validation or risk
    Pending --> Filled: fully matches on arrival
    Pending --> PartiallyFilled: partly matches on arrival
    Pending --> Live: no match, rests on book
    Live --> PartiallyFilled: incoming order matches part
    Live --> Filled: incoming order matches all
    Live --> Canceled: cancel request honored
    Live --> Expired: time in force runs out
    PartiallyFilled --> Filled: remainder matches
    PartiallyFilled --> Canceled: remainder canceled
    PartiallyFilled --> Expired: remainder expires
    Rejected --> [*]
    Filled --> [*]
    Canceled --> [*]
    Expired --> [*]
    
The complete order lifecycle. Terminal states (Rejected, Filled, Canceled, Expired) are absorbing — no arrows leave them. Note what's missing: no transition out of Filled. A filled order can never be canceled, no matter when the cancel was sent.

The diagram resolves the classic race by construction. Alice sends a cancel at the same moment Bob's order is matching hers. Both requests enter the engine's single input stream (1.3); whichever the engine processes first wins, and the state machine makes the loser's outcome well-defined:

  • Cancel first → order is Canceled; Bob's order matches elsewhere or rests.
  • Match first → order is Filled; the cancel arrives to find no live order and returns too late to cancel — an ordinary, expected response, not an error.

This is worth internalizing as a method: races don't get resolved by cleverness at the edges; they get resolved by total ordering at the core, plus a state machine with no undefined cells.

Key ideas
  • Orders live in an explicit state machine with absorbing terminal states.
  • Exactly one writer — the engine — performs transitions; all else requests.
  • "Too late to cancel" is a normal answer, designed in, not an error bolted on.
Next step

See what actually stuck.

Take the practice scenarios now.