# Compensation Is Not Rollback: Design Sagas for Reality

> How to design saga pattern compensations for real systems: business undo actions, unknown external outcomes, durable step state, and manual recovery paths.

Canonical URL: https://www.devobs.io/articles/saga-compensation-design/
By: Amara Okafor
Published: 2025-09-14T12:23:36.500Z
Updated: 2026-09-06T10:18:15.722Z
Section: Architecture

## Define business compensation for each saga step

A saga does not rewind time. Each completed step needs a business compensation that may differ from its technical inverse, and the state machine should represent compensation failure and human resolution explicitly. The [Azure compensating transaction pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction) is a useful reference for the constraints and failure modes of compensation in eventually consistent workflows.

*Review date: 2026-09-06*

## Work through one concrete case

Consider a reservation, payment, and notification flow. A reservation can often be released, a captured payment may require a refund rather than an uncapture, and an email cannot be unsent. If notification fails after payment succeeds, the right action may be retry plus support visibility rather than an automatic refund. The [Azure compensating transaction pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction) explicitly notes that compensation is application-specific, may require business rules, and does not always mean undoing every step in exact reverse order.

Store every step transition durably, along with the identifiers you need to correlate the local saga with external side effects. [Azure’s guidance](https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction) recommends recording information about each step and how to undo it, recording compensation progress so it can resume after failure, and defining steps as idempotent commands so retries are safe. [Temporal’s compensating-actions guidance](https://temporal.io/blog/compensating-actions-part-of-a-complete-breakfast-with-sagas) adds a practical example of failures that happen after an external effect occurs but before the coordinator has safely recorded success.

## Design compensations in business terms

A practical design choice is to register the compensation before starting the effect it covers, so a crash, timeout, or cancellation between the external side effect and local persistence still leaves you with a safe recovery path. [Temporal’s compensating-actions guidance](https://temporal.io/blog/compensating-actions-part-of-a-complete-breakfast-with-sagas) demonstrates this ordering with compensations such as "put bowl away if present," specifically to handle cases where the forward effect may or may not have happened.

That is why compensation logic should be written in terms like “release reservation if present” or “refund payment if captured,” not as a blind inverse of the last local step. The [Temporal article](https://temporal.io/blog/compensating-actions-part-of-a-complete-breakfast-with-sagas) shows that compensations must tolerate uncertain execution state, and the [Azure compensating transaction pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction) warns that concurrent work and intermediate state changes make naive rollback unsafe.

The right compensation depends on the external system’s recorded state, not merely the coordinator’s last response. This follows directly from the [Azure compensating transaction pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction), which explains why restoring an earlier snapshot can overwrite concurrent changes and why compensation must account for application-specific rules.

Persist saga ID, step, attempt, external reference, outcome, and next action. Only one coordinator should advance a saga version at a time. When a timeout leaves the external outcome unknown, reconcile with the provider first; [Azure’s guidance](https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction) notes that a step might not fail immediately and can instead get blocked, so timeout handling should not assume the side effect either definitely happened or definitely did not.

## Make failure a supported state

Compensation itself can fail. Model states such as compensating, retrying, blocked, and manual review, with an owner and a safe retry rule for each. The [Azure compensating transaction pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction) states that compensating transactions are eventually consistent operations and can fail themselves, sometimes requiring manual intervention with detailed alerting.

Order compensations by business risk, not habit. You may need to stop fulfillment before refunding, or preserve a reservation until payment status is confirmed. [Azure](https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction) notes that compensations might not need to run in strict reverse order and that some undo steps can run in parallel, while [Temporal’s compensating-actions guidance](https://temporal.io/blog/compensating-actions-part-of-a-complete-breakfast-with-sagas) gives a practical pattern for registering safe compensations before each forward effect.

Test the ugly cases on purpose: a crash after the provider commits but before local persistence, duplicate messages, compensation timeout, and an irreversible notification. The operator view should show what happened externally, what the saga believes happened, and which action is safe next. Start with one saga and write a table of forward effect, observable confirmation, compensation, correlation data, and manual fallback for every step.

## Source references

- <https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction>
- <https://temporal.io/blog/compensating-actions-part-of-a-complete-breakfast-with-sagas>
