Autosave for your business
If you’re a long-time video game player like me, your muscle-memory vividly remembers the F5 key’s location on your keyboard. For everyone else: F5 is a common key-binding for “quicksave” in computer-games. And like many others, I learned how to use it the hard way. After spending hours sneaking through dungeons, battling orcs, and looting valuable treasures, some nasty troll made an unexpectedly quick end to my character. That’s when I would realize that I hadn’t saved my game for a very long time and had to start over. From that moment on, I’d save my game as often as I could, and F5 became my closest ally.
Modern games now provide a built-in feature called autosave. These games save your progress automatically now and then so that you won’t lose all your progress—only a few minutes at worst. This might sound trivial and obvious, but it is a game-changer for player experience. The player can now focus on solving their mission rather than the mechanics of the game itself.
Why are we talking about video games when we have important business (let’s say, selling video games) to do? Let me ask a different question: what if that nasty end boss doesn’t come in the form of a troll? What if the end boss is a network error, power outage, concurrency conflict, or even squirrels?
🔗Real-life end bosses
Let’s get a bit more concrete with some simple pseudo-code for a retail order processing system:
// Some API invoked by the customer (e.g. RESTful API)
ProcessNewOrder() {
CollectPayment();
UpdateWarehouse();
ShipOrder();
}
Even from this minimal code, we can see several systems at play. There’s payment processing, inventory, shipping, and very likely a customer management system. Some of them might support transactions, others may not. But failures can happen in any of them, so your code needs to be robust enough to handle every potential failure gracefully, or you might lose the order. Game Over!
Let’s say a network troll hits us in the middle of this code, perhaps in the UpdateWarehouse
call. Ignoring the question of what happens to the already-completed payment, what do we tell our poor customer who has gone through the trouble of selecting items, adding their shipping and payment information, and submitting the order? Do we tell them “Something went wrong, please enter your information again or try again later”? I’m not sure about you, but my initial reaction when I had to restart a level from the beginning was often to throw the controller across the room and “gently” turn off my game.
Instead, we’ll add an autosave into our business process by introducing a messaging pattern. Messages introduce a natural savepoint for failures. Once a message is queued, the processing of the message can fail at any point but the message will remain in the queue until the process is successful. When an error occurs, you can “restart” at the beginning of the process without losing any progress/state that was made before the message was queued.
Let’s see how this looks in practice, again with pseudo-code:
// Some API invoked by the customer (e.g. RESTful API)
ProcessNewOrder() {
// queue a message to process the order
SendMessage<OrderReceived>();
// order is now saved in the queue; we can't lose the order
}
// We will restart from here if processing fails
HandleMessage<OrderReceived>() {
CollectPayment();
UpdateWarehouse();
ShipOrder();
}
This is already better. If an error occurs anywhere in the HandleMessage
method, we haven’t lost any information. All our message details (i.e. the order information) are still intact on disk in the message queue. Yes, the order hasn’t gone through but we’ve only delayed it until we can investigate the problem. We don’t need to go back to the customer and tell them to “restart”. We introduce a message (think: savepoint) as early as possible and avoid the worst case scenario: losing an order.
From now on, every failure will allow us to retry the operation.
Now let’s add some upgrades.
🔗Leveling up
In video games, the more abilities your enemy has, the more difficult it will be and the more likely we are to fail and return to our last savepoint. In our scenario, that means the more resources and systems involved in a business process, the higher the total chance of failure becomes. But unlike in video games, we have the power to introduce savepoints wherever we want. We can add as many savepoints to your business process as we want. We just need to split the individual tasks into separate, more granular message handlers.
Let’s insert some more savepoints into our ordering process:
// Some API invoked by the customer (e.g. RESTful API)
ProcessNewOrder() {
SendMessage<CollectPayment>();
// order is now saved in the queue. We can't lose the order anymore.
}
HandleMessage<CollectPayment>() {
CollectPayment();
SendMessage<UpdateWareHouse>();
// Payment data is now saved. We can't lose that information now.
}
HandleMessage<UpdateWareHouse>() {
UpdateWarehouse();
SendMessage<ShipOrder>();
// The warehouse request is now saved.
}
HandleMessage<ShipOrder>() {
ShipOrder();
}
This is a bit more code than before, but let’s see what we get for that verbosity.
At every step in the process, we do a small bit of work and then we send another message to signal that we need to perform the next step. I.e. we add a message to a queue to be picked up by the next handler. If something goes wrong in the handler, we haven’t lost any information; instead, the message is moved to an error queue. There, we can review the message and see what went wrong, then fix it and requeue it for processing, restarting from our “savepoint” rather than having to restart from the beginning of the process.
🔗The final boss
We’ve only scratched the surface of how a messaging-based architecture can provide a better experience for your users. Even better, there are frameworks available, such as NServiceBus, that have autosave and other functionality built right in so you can focus on writing the code that matters rather than infrastructure.
To see how autosave works in action, try ParSEC, an interactive tutorial where you’ll be using messaging concepts to search distant planets for resources.
No quicksave key required.