Saga compensation design
Map each committed effect before defining its compensation
Design the recovery record from the actual side effects. A list of service calls does not tell the coordinator what can safely be corrected after failure.
In this article
Write down the effect of each step
For stock reservation, record the reservation identifier and quantity held. For a payment operation, retain the provider's operation reference and confirmed state. For carrier booking, store the booking identity and any relevant cancellation condition.
Keep intent, attempted call and confirmed effect separate. A submitted request with no response belongs in an uncertain state until the service contract provides enough evidence to resolve it.
Use these records to define the recovery operation. Releasing a specific reservation is more precise than setting total stock back to an earlier value.
Define forward and recovery states
Create a durable state model that distinguishes pending, completed, uncertain, compensating and unresolved outcomes. The exact names can vary, but operators and code need the same meaning.
{
"sagaId": "fulfilment-example-18",
"step": "reserveStock",
"operationId": "reserve-example-4",
"state": "completed",
"effectRef": "reservation-example-91",
"compensation": {
"operationId": "release-example-4",
"state": "notStarted"
}
}This example illustrates one step record. A complete workflow also needs versioning, ownership, timestamps and rules governing which transitions are allowed.
Make operations repeatable under their contracts
Use stable identifiers for forward and compensating calls. A retry should refer to the same intended release or reversal, not create a new action each time.
Where the target service lacks an idempotent operation or reliable status lookup, document the uncertainty and manual reconciliation required. The coordinator cannot invent an exactly-once guarantee around an opaque remote effect.
Apply current eligibility checks before compensation. A reservation may already have been consumed or a booking may no longer be cancellable. Those outcomes need business handling, not endless retry of a permanently invalid command.
Persist before moving to the next step
Record the resolved outcome before dispatching dependent work. On restart, the coordinator should determine the next action from durable state rather than re-executing the whole sequence.
Prevent concurrent coordinators from advancing the same saga inconsistently. Use the storage system's version or ownership mechanism and test stale workers.
Finally, exercise a forward failure, uncertain result and failed compensation. Inspect the final business records in each service. The implementation should show which effects remain and why, even when automatic recovery cannot finish the workflow.
Primary sources
Microsoft Learn: compensating transactionsAWS Builders' Library: idempotent APIsReferences checked 11 September 2026.