Interview Prep Zoneby CuriouserLabs

Question 2 of 5 · senior level

Checkout spans order, payment and inventory services. Design it as a saga — and be honest about what you gave up.

🎤 Say this first

A saga replaces one distributed transaction with a sequence of local transactions, each of which commits independently, plus a compensating action for each step so a later failure can be semantically undone. Create order (pending) → charge card → reserve stock → confirm order; if reserving stock fails, run the compensations backwards: refund the charge, cancel the order. Two shapes: orchestration, where an explicit coordinator (a state machine, Temporal, Step Functions) drives the steps and owns retries and timeouts, and choreography, where each service reacts to the previous one's event and no one owns the flow. What you give up is isolation: every intermediate state is visible to everyone, so other transactions can read and act on a half-finished saga. You keep A, C and D — you lose the I.

The full picture

OrchestrationChoreography
Flow lives inone coordinator, explicitlyemergent from event subscriptions
"Where is order 123 stuck?"query the state machinecorrelate logs across N services
Couplingcoordinator knows every participantservices know only events
Retries, timeouts, compensation ordercentralized, testableduplicated in every handler
Good forflows with real branching, money, SLAs2–3 steps, simple fan-out reactions
Compensations are business operations, not rollbacks
// Each step is its OWN committed local transaction. There is no global undo,
// so every step ships with the business action that semantically reverses it.
saga.step("createOrder",   this::createPending,   this::cancelOrder)
    .step("chargeCard",    this::charge,          this::refund)
    .step("reserveStock",  this::reserve,         this::releaseReservation)
    .step("confirmOrder",  this::confirm,         null);   // last step needs none

// A refund is NOT a rollback: the charge really happened, the customer really
// saw it on their statement, and the refund is a second visible event. That is
// the honest description to give an interviewer.

// Ordering rule that matters: put the steps you CANNOT compensate last.
// "Send the shipping notification" belongs after everything reversible,
// because there is no un-send.
  • Order steps by reversibility, and say why: fully reversible first (a database row), then costly-but-possible (a refund carries fees and support load), then irreversible last (email sent, package shipped, third-party API with no cancel). Many production sagas are simply the same steps in a smarter order — that's a design insight, not a framework feature.
  • Every step and every compensation must be idempotent and retryable, because the coordinator will re-drive them after a crash. A compensation that fails is not a corner case — it's the normal case at scale — so compensations need retries, a dead-letter path and an alert, not a catch block that logs and moves on.
  • Pivot transactions are the concept worth naming: the step after which the saga can no longer be abandoned, only driven forward. Before the pivot you compensate backwards; after it you retry forwards until it succeeds. Knowing where your pivot is tells you which failures need a refund and which need an on-call engineer.
  • Choreography's real cost shows up on day 200, not day 1: with events triggering events, nobody can answer 'what happens when a payment succeeds?' without reading every service. Orchestration trades a bit of coupling for a flow you can draw, test and query — which is why Temporal and Step Functions won this argument in practice.
  • The saga is not a smaller 2PC — it is a different guarantee. 2PC gives you atomicity with no visible intermediate state and blocking on failure. A saga gives you eventual atomicity with very visible intermediate state and no blocking. Say which property your domain actually needs rather than presenting the saga as a strict upgrade.

🔄 Likely follow-up questions

  • Where is the pivot in a checkout saga, and how does it change your failure handling?
  • What happens when a compensating transaction itself fails repeatedly?
  • When would you choose choreography over orchestration despite the visibility cost?