fivenines
9/38
Lesson 1.7 99.9%

Trip lifecycle: the state machine of record

🎯 Objectives
  • Model the trip as an explicit state machine with guarded, event-driven transitions.
  • Record the actual route for fare calculation, receipts, and disputes.
🔗 Connect

Same pattern as driver onboarding (1.2): long-running process = persisted state machine. A trip is shorter (minutes not days) but higher stakes: money (1.8) and safety hang off its transitions. Matching (1.6) hands us a trip in Assigned.

The Trip service is the system of record for what actually happened. Every transition is an immutable event row (who, when, where) appended alongside the current-state column — cancellation fees, fare calculation, support disputes, and regulatory reports (1.13) all replay this record. States and guards:

stateDiagram-v2
    [*] --> Requested : rider confirms quote
    Requested --> Assigned : matcher assigns driver (1.6)
    Requested --> NoDriver : candidates exhausted
    Assigned --> Arriving : driver en route to pickup
    Arriving --> Waiting : driver within 50 m of pickup
    Waiting --> InProgress : driver taps start — GPS at pickup
    InProgress --> Completed : driver taps end — fare finalized
    Assigned --> CancelledByRider : may incur fee after grace period
    Arriving --> CancelledByRider : fee if driver near
    Waiting --> CancelledByDriver : no-show after 5 min wait
    Assigned --> CancelledByDriver : penalized — re-dispatch rider
    Completed --> [*]
    NoDriver --> [*]
    CancelledByRider --> [*]
    CancelledByDriver --> [*]
The trip state machine. Guards (GPS proximity, wait timers, grace periods) prevent state transitions from being taken on faith.

Two design rules keep this robust. First, transitions are commands with guards, not notifications: "start trip" is rejected unless the trip is in Waiting and the driver's GPS is near the pickup — the state machine defends itself against buggy or malicious clients. Second, during the trip, the location stream (1.4) is forked: pings from a driver on an active trip are additionally appended to that trip's route trail, which becomes the billing-grade distance record at completion. Quotes estimated with cell math; fares settle on the recorded route.

sequenceDiagram
    autonumber
    participant D as Driver app
    participant T as Trip service
    participant L as Location service
    participant R as Rider app
    participant P as Payments (1.8)
    D->>T: start trip
    T->>T: guard — state Waiting, GPS at pickup? OK → InProgress
    T->>R: trip started
    loop every 4 s during trip
        D->>L: ping
        L->>T: append to trip route trail
        T-->>R: live position + updated ETA
    end
    D->>T: end trip
    T->>T: guard OK → Completed. Fare = rates × recorded route
    T->>P: capture fare {trip id, amount}
    T-->>R: trip complete — receipt pending
    T-->>D: earnings updated
Worked example: the in-progress trip. The same pings that animate the rider's map become the immutable billing record.
⚠️ Pitfall

GPS lies: tunnels, urban canyons, phones dying mid-trip. The trail recorder must tolerate gaps (interpolate for billing, flag large gaps for review) and the state machine must allow ops to repair a trip stuck in InProgress because a phone died at the destination. "What does support do when this wedges?" is a design requirement, not an afterthought.

Next step

See what actually stuck.

Take the practice scenarios now.