We had an initial vision for canceling a contract: choose the contract, input an effective date, and click Cancel.
Then users wanted to get a preview of the cancellation, with all fully-calculated refund values. We had the preview write everything to the database, and if the user confirmed it, we would mark the cancellation as “complete.” A user could also “revoke” cancellation, which would delete the pending cancellation.
Soon, we had a system to recover abandoned cancellations by reminding users they had one pending, and auto-revoke it after a deadline.
Then it became clear that there could be race conditions. What if the cancellation is processed between receiving the notification and following the link enclosed? What if someone else was running the cancellation on the same contract simultaneously, and ended up with the same results? We needed to show the new status.
What if someone made a mistake with the inputs? We needed an “Edit” button that would go back, as well. If there’s an Edit button, and someone comes in from the main menu, should we reload the inputs, or skip the input screen entirely?
It wasn’t long before Accounting wanted a “reinstate” button. Suddenly, the “final” state was no longer final. In the mean time, we had integrated automatic data pushes to the actual accounting software, which meant a huge mess if they wanted to reinstate one that was on the official record as canceled.
If there’s a moral to this, it’s that any software pipeline should be fully reversible, even into the parts where “we sent money out, and we have to ask for it back as a consequence.”