Sagas vs Two-Phase Commit: Pick Your Poison for Distributed Transactions
An order flow that touches payments, inventory, and shipping wants to be a transaction, all or nothing, and it cannot be one, because the data lives in three services. Your options are two phase commit, sagas, or pretending, and most production systems that claim option one or two are secretly running option three.
Two phase commit is the honest attempt at real atomicity. A coordinator asks every participant to prepare, and only when all vote yes does it tell them to commit. The guarantee is genuine. The price is that between prepare and commit, every participant sits holding locks, waiting, and if the coordinator dies at the wrong moment they wait indefinitely, blocked, refusing other work. You have made your throughput hostage to your slowest participant and your availability hostage to the coordinator. Inside one database cluster, fine. Across microservices owned by three teams over a network, it is a seance.
Sagas trade the guarantee for survivability. Break the transaction into local steps, each committing immediately, and pair every step with a compensating action that undoes it. Charge the card, reserve inventory, book shipping, and if shipping fails, release the inventory and refund the card. No locks held across services, no coordinator to grieve. The cost is that the world can observe intermediate states, the card shows a charge before the refund lands, and compensation is business logic you must design, because a refund is not an un-charge, it is a second event with its own failure modes.
The actual decision, in my experience: how bad is a visible intermediate state? Money movement between banks earns 2PC's misery. An order flow where "charged then refunded" is an acceptable Tuesday takes the saga. Just also write the reconciliation job that finds the flows where compensation itself failed. That job is the real transaction manager.