pattern

Saga

also called Compensating Transaction, Long-Running Transaction

A sequence of local transactions across services where failure is handled by compensating actions rather than by rollback.

sagadistributed-transactionscompensationdoordashworkflow

Definition

When a business operation spans several services with separate databases, a single ACID transaction is unavailable. A saga decomposes it into local transactions, each committed independently, with a compensating action defined for each step that can undo its business effect.

Two coordination styles:

  • Choreography — each service reacts to the previous one's event. No central component, low coupling, and the overall flow exists nowhere except in the heads of the people who built it. Fine for three steps, unmanageable at eight.
  • Orchestration — a coordinator holds the state machine and issues commands. The flow is explicit, inspectable and testable; the coordinator is a component to build, own and make highly available.

For anything with more than a handful of steps, or with a support team that needs to answer "where is order 41,229 stuck?", orchestration is almost always the right choice.

Why it matters

The alternative — a distributed two-phase commit across services — takes locks for the duration of a network round trip and blocks indefinitely if the coordinator dies mid-protocol. It converts several independent availability numbers into their product. Most organisations that try it end up with an outage that teaches them why sagas exist.

Industry example

A food delivery order is the textbook case: create the order, authorise the payment, reserve restaurant capacity, dispatch a courier, then confirm. Each step lives in a different service with its own store, and the steps take seconds to minutes rather than milliseconds.

What makes DoorDash-style dispatch instructive is that the compensations are not symmetric. Cancelling a payment authorisation is clean. Un-dispatching a courier who has already driven to the restaurant is not — the business owes them something. Un-cooking food is impossible. So the architecture cannot simply "roll back"; it must order the steps so that the irreversible ones come last, and it must model the residue explicitly: a courier compensation payment, a refund policy, a customer credit.

That is the real design work. The saga framework is the easy part.

Implementation patterns

  • Order steps by reversibility. Cheap-to-undo first, irreversible last. Take payment authorisation early (reversible) and capture late (harder).
  • Make every step idempotent. The orchestrator will retry, and retries are the normal case.
  • Persist state before each command. The coordinator must be able to resume after a crash and know exactly which step was in flight.
  • Set a timeout per step with a defined expiry action, because a step that never answers is far more common than one that fails.
  • Use semantic locks for the window where state is provisional — an order in PENDING_PAYMENT is visibly not final, so other processes do not treat it as complete.

Failure scenarios

  • The compensation fails. Now what? There must be a terminal state that raises a human alert. Sagas do not eliminate manual intervention; they bound it and make it visible.
  • A step is not truly reversible but was assumed to be — an email was sent, a message was posted to a customer, funds were captured.
  • Choreography sprawl. Nobody can draw the flow; a new step is added by a team that does not know the whole sequence, and an unrelated consumer breaks.
  • Concurrent sagas over the same entity. Two cancellations arrive at once. Without a semantic lock or a state machine that rejects invalid transitions, both compensate and the customer is refunded twice.

Trade-offs

You gain availability — services stay independent, no distributed locks, each step retries on its own schedule — and you pay in intermediate states visible to users, compensation logic that roughly doubles the workflow code, and a much harder testing problem: every step can fail at every point, including during compensation.

When the operation is short, entirely within one bounded context, and correctness is paramount, a single database transaction is dramatically simpler and should be preferred. Sagas are for when that option is genuinely gone.

Interview question

"Design order fulfilment as a saga across payment, inventory and delivery. Which step do you make last, and what is your terminal state when a compensation itself fails three times?"