On this page
  1. What does each pattern actually do?
  2. Side-by-side comparison
  3. How a saga runs
  4. When each is the right answer
  5. What both patterns require from your code
  6. Where AI coding assistants get this wrong
  7. Checklist
  8. FAQ
    1. Can a saga guarantee consistent data?
    2. Why not simply use two-phase commit everywhere?
    3. What is a compensating action?
    4. Do I need a framework?
  9. Related topics
  10. Sources
comparison

What Is a Saga Pattern (and How Is It Different From Two-Phase Commit)?

Sagas coordinate a multi-service operation through local transactions and compensating actions; two-phase commit uses a coordinator that blocks. Which one fits depends on consistency needs.

Quick answer

  • A saga splits a distributed operation into a sequence of local transactions, each with a compensating action that undoes its effect if a later step fails.
  • Two-phase commit coordinates participants through a prepare-then-commit protocol, and every participant must be available at commit time.
  • A saga is eventually consistent and never holds locks across services; two-phase commit is atomic but blocking and fragile when a participant is unavailable.
  • Sagas are the usual choice across services owned by different teams; two-phase commit suits a small set of participants inside one trust and availability domain.
  • Neither is a substitute for idempotency: saga steps are retried and compensations may run more than once.

What does each pattern actually do?

A saga models a multi-step business process as a sequence of local transactions. Each step commits its own changes in its own service, and for each step there is a compensating action that reverses its effect. If step four fails, the saga runs the compensations for steps three, two, and one, in reverse order, undoing the work already committed. Nothing is ever rolled back in the database sense — the earlier steps really did happen, and the compensation is a new transaction that counteracts them.

Two-phase commit takes the opposite approach: it tries to make the whole operation atomic. A coordinator asks every participant to prepare, each participant locks what it needs and votes, and if all vote yes the coordinator tells them to commit. Only then are the changes durable. The cost is that participants hold locks between the two phases and must remain available for the commit, so a single unresponsive participant can block the entire transaction.

Side-by-side comparison

The row that decides most designs is the failure behaviour: a saga makes progress and repairs, while two-phase commit waits. Under a partial outage, waiting is often worse than repairing.

| Attribute            | Saga                                  | Two-phase commit                        |
|----------------------|---------------------------------------|-----------------------------------------|
| Consistency          | Eventually consistent                 | Atomic across participants              |
| Locking              | Local locks only, released per step   | Locks held across the whole protocol    |
| Failure handling     | Compensating actions, run in reverse  | Rollback of prepared changes            |
| Blocking risk        | Low                                   | High while a participant is unavailable |
| Partial visibility   | Intermediate states are observable    | Changes appear atomically or not at all |
| Coordination         | Orchestrated or choreographed         | A coordinator and a protocol            |
| Best for             | Cross-service business processes      | Few participants, one domain, short work |
| Complexity lives in  | Designing compensations               | Coordinator availability and recovery   |

How a saga runs

An orchestrated saga has a central coordinator that calls each step and, on failure, calls the compensations. A choreographed saga has each service emit an event that the next service reacts to, with no central point of control. Orchestration is easier to reason about and to observe; choreography avoids a single coupling point and becomes hard to follow once there are more than a few steps. Either way, the logic that matters is the pairing of each step with its compensation.

Compensations are not always symmetric. Reversing a payment means issuing a refund, not deleting a charge; reversing a sent email is impossible. Where a step cannot be undone, the design has to put it last, or make the earlier steps provisional — reserving rather than confirming — so that a failure before the point of no return has something meaningful to undo. This is why saga design is business modelling as much as technical work.

Place an order (orchestrated saga)
  1. reserve inventory        <-> release inventory
  2. authorise payment        <-> void authorisation
  3. create shipment          <-> cancel shipment
  4. capture payment          (point of no return; cannot be compensated)
  5. notify customer          (irreversible; placed last)

On failure at step 3: cancel shipment, void authorisation, release inventory.
If the coordinator crashes, it resumes from a persisted saga state,
and each step must be idempotent so a replay is safe.

When each is the right answer

Choose a saga when the steps belong to different services, when the process is long-running, or when blocking is unacceptable — which covers most cross-team, cross-database workflows. Choose two-phase commit when the participants are few, inside one administrative domain, hold a strong trust relationship, and the transaction is short, because then the blocking window is small and atomicity is worth it. Many databases support two-phase commit precisely for the case of a couple of closely coupled resources.

Do not use either when a single database can hold the whole operation. A transaction inside one database is the simplest correct thing, and both patterns exist only because the data is spread across systems that cannot commit together. If the work could be colocated, the cheapest fix is to move it rather than to coordinate it.

What both patterns require from your code

Steps must be idempotent, because both retry under failure. Saga steps are retried by the coordinator, and two-phase commit participants can be asked to commit after a timeout. If a step performs a non-idempotent side effect, a retry duplicates it, which is the same failure the pattern was introduced to prevent. Every step therefore needs a stable operation id and a check for whether the work is already done.

Both also need durable state. A coordinator that loses its record of which steps completed cannot compensate correctly, so the saga’s progress must be persisted, and the two-phase protocol’s decisions must be recoverable. That state is the reason these patterns are usually introduced with a framework or a workflow engine rather than by hand — the interesting part is recovery, and recovery is where hand-rolled implementations fail.

Where this bites vibecoders

An assistant asked to “make this work across services” will sometimes propose a distributed transaction or a saga without saying that intermediate states become visible to users or that every step needs a compensation. The convincing-looking part is the happy path; the part that decides whether it works is what happens when step three fails at the same moment as a deploy. Asking “what does the user see between step two and step four?” surfaces the design gap immediately.

Where AI coding assistants get this wrong

  • Treating a saga as if it were atomic, so intermediate states are never accounted for.
  • Designing steps without compensations, then discovering there is nothing to undo.
  • Putting an irreversible step before a compensable one.
  • Proposing two-phase commit across services that cannot guarantee availability.
  • Forgetting that saga steps and compensations can run more than once.
  • Keeping coordinator state only in memory, so a restart loses the saga’s position.

Checklist

  • Confirm the operation genuinely spans systems that cannot commit together.
  • Write the step list and the compensation for each step.
  • Order steps so irreversible actions come last.
  • Persist the saga’s state so a restart can resume or compensate.
  • Make every step and compensation idempotent.
  • Decide what a user sees during the intermediate states.
  • Alert on sagas that are stuck part-way rather than only on failures.

FAQ

Can a saga guarantee consistent data?

It guarantees that the process either completes or is compensated, which is not the same as the data always being consistent as observed by a reader. During the saga, intermediate states are visible: an order exists before it is paid. Designs that cannot expose those states need to present them explicitly, for example with a pending status, rather than pretending the operation was atomic.

Why not simply use two-phase commit everywhere?

Because it blocks. Participants hold locks from prepare until commit, and the coordinator must be able to reach all of them; an unavailable participant stalls the transaction and the locks with it. Across services owned by different teams, with different availability, that is a recipe for an outage that a saga would have ridden out.

What is a compensating action?

A new transaction that counteracts a committed step — releasing a reservation, voiding an authorisation, issuing a refund. It is not a rollback: the original change really happened and may have been observed. Compensations therefore have to be chosen with the business meaning in mind, and some actions cannot be compensated at all, which is why they belong at the end.

Do I need a framework?

For more than a couple of steps, usually yes. The happy path is easy to write by hand; the durability of the coordinator’s state and the recovery of a partially completed saga are not. A workflow engine or an established saga library already handles persistence, retries, and resumption, and that is where the value is.

Sources

Share: