DEV Community

Cover image for Compensation Is Not a Rollback, It Is New Business Work
Krishnam Murarka
Krishnam Murarka

Posted on Originally published at edilec.com

Compensation Is Not a Rollback, It Is New Business Work

The word that does the most damage in a long-running workflow review is rollback. A step fails somewhere in the middle of an order or an onboarding, someone asks for the whole instance to be rolled back, and the room nods, because inside a single database that sentence has a precise and nearly free meaning. The engine discards uncommitted work and the world looks as though nothing happened.

None of that meaning survives the trip across a service boundary, or across a week. By the time a workflow is long-running, every earlier step has already committed somewhere. There is no uncommitted state left to discard. Some of the effects have also left the building: money moved, stock was reserved, a supplier was given a date, an account was provisioned. A database can be told to forget. A supplier cannot.

So the thing we actually do when a later step fails is not reversal. Cancelling a shipment, issuing a refund, releasing a reservation, marking a record void: each of those is a new action that creates new facts, and each one may carry a fee, a delay, or a consequence that cannot be taken back either. Compensation is forward work that happens to have a corrective purpose. Once we started writing it down that way, several things that had been arguments turned into design decisions.

The first is that every forward activity needs a classification before it ships, not after it fails. We label each one compensable, retryable, or a pivot. A pivot is the step after which there is no sensible way back and the workflow has to be driven forward to some consistent outcome instead. Teams tend to discover their pivots during an incident, which is the worst possible moment to be having a conversation about whether a reversal is even permitted. Writing the label next to the activity forces the question early, while it is still cheap to answer.

The second is that the information needed to compensate has to be captured when the forward action succeeds, not when a later one fails. Reservation identity, amount, terms, the version of the rules that applied, and the authority under which it was done. At failure time that context is often gone, or is spread across systems that have since moved on, and the recovery path ends up reconstructing it from logs. Recording it at the moment of success costs almost nothing and is the difference between a compensation that can run unattended and one that needs an engineer.

The third is that compensation is an ordinary activity and fails like one. It needs a stable operation identity, it needs to be safe to run twice, and it needs to be resumable, because a refund attempt can time out after the remote side has already accepted it. A compensation path that is not idempotent is a way of turning one bad day into two.

Order is the part most often assumed rather than designed. The instinct is to unwind the stack, undoing steps in exact reverse. Microsoft's guidance on the compensating transaction pattern makes the point that reversal need not follow the original order and that some compensations can run in parallel. What governs the order is the dependency between the effects and the risk each one carries, so that is what we encode. Mechanically walking backwards is a convenient default that quietly assumes every step was equally reversible.

And some are not reversible at all, or the reversal costs more than the original mistake. Those do not belong in an automatic path. They go to a person, as a durable task with an identity, an assignee, the permitted decisions, and the full ledger of effects that have already happened, so the judgement is made with the facts rather than by reading a stack trace. The disposition that person chooses is preserved with their authority and their reason, because it is the only record of why the instance ended the way it did.

Underneath all of it is a distinction that is easy to collapse. Not every failure should compensate. Transient failures are retried first, an alternative route is better when it still reaches the intended outcome, and compensation is for the case where effects that already completed have to be addressed. Completed, rejected, cancelled, expired and failed drive different customer messages and different accounting treatment, so a technical exception is not automatically a business rejection. Treating them as one is how a workflow ends up refunding a customer because a queue consumer restarted.

Designing the processes and the systems this runs on is what our enterprise systems practice does at Edilec. The full write-up, including the timeout policy and workflow versioning material this excerpt leaves out, is at Long-Running Workflow Orchestration: Timeouts, Compensation, and Human Tasks.

Top comments (0)