Blueprint
Fundamentals

Saga pattern

The only way to run a transaction across services you can't hold a database lock across — a state machine with a defined undo for every step.

01

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.

Say this out loud

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.

03

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.

Trade-offThe orchestrator is a single conceptual bottleneck for change. New saga steps mean orchestrator code changes; the coupling shows up in team boundaries.

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'.

Trade-offGreat for small sagas, terrible for complex ones. If you can't easily draw the state machine, you need an orchestrator.

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.

Trade-offStoring idempotency keys costs space; TTLs mitigate. The alternative — 'assume nothing ever retries' — makes every partial failure a support ticket.

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.

Trade-offCompensations often need human intervention on rare edge cases. The saga records the failure and flags it; ops finishes the job.
04

Numbers worth memorising

Steps per sagatypically 3-7 — more is a smell
Orchestrator state footprint~1 KB per active saga in Postgres
Retry policy default3 retries with exponential backoff, then compensate
Idempotency key TTL24-72 hours; longer for financial transactions
Median saga durationseconds to minutes; long-running ones (delivery) can span days
05

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.

Shows up in

Found a bug or want more?

Tell me what's broken, missing, or wrong. Every message lands in my inbox.