What it is
A saga is a sequence of local transactions where every step has a compensating action that undoes it. If step N fails, run compensations for N-1, N-2, … in reverse. You never hold a distributed lock or a two-phase commit; you accept intermediate visibility in exchange for cross-service atomicity of intent.
The interview relevance: any design that touches money, external APIs (Stripe, Twilio, S3) or multiple microservices must have this pattern in its vocabulary. Interviewers score you on knowing that 2PC is impractical against systems you don't own, and that sagas are the pragmatic answer.
For checkout I'd model this as a saga — reserve inventory, charge payment, create order, notify shipping. Each step is local, each has an undo, and an orchestrator holds the state machine.
The variants and when to pick each
Orchestration — one brain drives the sequence
A dedicated orchestrator service holds the saga state machine, persists progress after each step to survive crashes, and drives each service in turn. All the business logic of 'what happens if X fails' lives in one place, easy to test and audit. This is how Amazon, Uber and most large e-commerce systems run their checkout.
The orchestrator becomes a small durable workflow engine — Temporal, Cadence, AWS Step Functions are off-the-shelf options; a homegrown one is a table plus a scheduler.
Choreography — events drive the sequence
No orchestrator. Each service publishes events that other services react to. order.placed triggers the inventory service, which publishes inventory.reserved, which triggers the payment service, and so on. Compensations are also events (payment.failed triggers inventory to release).
Attractive because it's loose coupling; every service knows only its own events. Falls apart when the saga has more than 3-4 steps — the flow becomes unreadable and hard to debug because there's no single place that says 'here is the sequence'.
Idempotency keys — the mandatory partner
Every step in a saga is a call to a service that might return 'timeout, unknown state'. The orchestrator retries. Without idempotency, retries double-charge, double-book, double-reserve. Every step must accept a unique idempotencyKey; the receiving service checks a table and returns the previous result if seen, or executes and stores if new.
This is the same pattern payment gateways use — Stripe's idempotency-key header exists precisely for saga participants.
Compensations are not always inverses
A key mental shift: a compensation isn't 'undo the write' — it's 'make the world look as if the step never happened, from the user's point of view'. Refund the charge (not delete the charge — the audit trail must survive). Mark the order cancelled (not delete it). Emit an inventory-released event (not roll back the row). Compensations are business logic, and they can fail.
Design each compensation as a first-class step with its own retries and idempotency, and accept that some real-world sagas cannot be perfectly compensated — an email was sent, a package shipped, a payment cleared to the bank. Those become manual reconciliation.
Numbers worth memorising
| Steps per saga | typically 3-7 — more is a smell |
| Orchestrator state footprint | ~1 KB per active saga in Postgres |
| Retry policy default | 3 retries with exponential backoff, then compensate |
| Idempotency key TTL | 24-72 hours; longer for financial transactions |
| Median saga duration | seconds to minutes; long-running ones (delivery) can span days |
Where the interviewer pushes
First push: 'why not just do 2PC?' Because the payment gateway is an HTTP API not a database. Two-phase commit needs every participant to speak XA or a compatible protocol; Stripe, Twilio, S3 don't. Sagas exist because 2PC across systems you don't own is fiction, and even where it's possible it's fragile (blocks on failed coordinators).
Second push: 'what does the user see during a saga?' Intermediate states — 'processing your order' — must be first-class UI. The alternative is pretending checkout is atomic, and then a race between payment success and order commit produces a support ticket. Accept the visibility, show progress, and design compensations that leave a clean record if things fail.