A saga is a way to run a transaction that spans multiple services without holding one big lock across all of them. Instead of one atomic commit, a saga is a sequence of local transactions, each one committing on its own, with a compensating action defined up front in case a later step in the sequence fails. It is the pattern most teams reach for once two-phase commit stops being practical across service boundaries.
What changed in 2026
- Saga orchestration frameworks got more standardized. Durable-execution platforms and workflow engines absorbed most of the boilerplate that used to make orchestrated sagas painful to build by hand.
- The transactional outbox pattern became the default companion. Pairing sagas with an outbox table — write the event in the same local transaction as the state change, publish it separately — closed the classic dual-write gap that used to cause silent data loss.
- Choreographed sagas got easier to trace. Distributed tracing tooling improved enough that debugging an event-driven saga no longer means reconstructing the flow from logs by hand across a dozen services.
What a saga actually is
Picture booking a trip: reserve a flight, reserve a hotel, charge a card. Each of those is a separate service with its own database. A saga runs them as a chain: reserve the flight and commit immediately, reserve the hotel and commit immediately, then charge the card and commit immediately.
If the charge fails, the saga does not roll back the earlier steps in the database sense — it cannot, they already committed. Instead it runs the compensating transactions: cancel the hotel reservation, then cancel the flight reservation, in reverse order.
Orchestration vs choreography
| Style |
How it works |
Strength |
Weakness |
| Orchestration |
A central coordinator calls each service and decides the next step |
Easy to follow, one place to see saga state |
Coordinator is a new component to build, deploy, and scale |
| Choreography |
Each service publishes an event; the next service reacts to it |
No central coordinator, services stay decoupled |
Hard to see the whole flow; failure handling is spread across every service |
Most teams start with choreography because it feels lighter, then migrate parts of the flow to orchestration once the number of steps and failure paths grows past what event-chasing can debug cleanly.
Designing compensating transactions
This is the part that is genuinely hard, and it is business logic, not infrastructure. A compensating action has to be semantically valid, not just a database rollback. Canceling a hotel reservation after the fact is a real business action with its own rules, not an undo button. Some steps cannot be compensated at all — you cannot un-send a confirmation email, so sequence steps like that last, after everything that can still fail. And compensations can fail too: design retries and a dead-letter path for them, or a stuck saga becomes a stuck refund.
When to use a saga
Reach for a saga when a transaction must span services you do not want to, or cannot, lock together — this is the same boundary problem covered in our two-phase commit guide. Skip it when everything the transaction touches lives in one database, when the steps are not naturally reversible and the business cannot tolerate a temporarily inconsistent state, or when you only have two or three steps and can get away with a simpler retry-with-idempotency approach.
Common mistakes
Treating a saga like a transaction with rollback. There is no rollback. Every compensating action must be designed and tested as its own operation, including its own failure modes.
Skipping idempotency. Steps and compensations both need to be safe to retry. Without idempotency keys, a network retry can double-charge a card or double-book a room.
Building a saga for something that fits in one database. The saga pattern adds real complexity — state tracking, compensations, eventual consistency. Do not pay that cost for a transaction that a single ACID commit would handle cleanly.
FAQ
Is a saga the same as a distributed transaction?
Not in the two-phase commit sense. A saga achieves an end result similar to a distributed transaction — all-or-nothing outcome — but through local commits and compensation instead of a global lock.
What happens if a compensating transaction fails?
It needs its own retry and escalation path, typically a dead-letter queue and manual or automated intervention. A saga design is incomplete until compensation failure is handled.
Should I use orchestration or choreography?
Orchestration for sagas with several steps, conditional branches, or where you need a clear view of state. Choreography for short, simple chains where you want services to stay fully decoupled.
Do sagas provide strong consistency?
No, they provide eventual consistency. There is a window where some steps have committed and others have not. Design your reads to tolerate that window, or surface a pending state to the user.
Where to go next