It so happened that the system I am currently working on has a lot of parts that communicate in asynchronous manner. In part it’s a result of technology choice, in part — of environment in which system operates. All projects I’ve worked on before have been synchronous and fairly straightforward: to each request we always expected a reply, even if reply was an error code or message. And over years of software development it has become a part of my mindset.
If you can always rely on receiving a response to your request or query, except client and server error situations, software development and user experience is more or less streamlined. It is especially straightforward in simple server-client systems, such as servers serving web pages, or APIs. But in fact, although it feels almost natural in programming, it’s not as common in the natural world. One could argue that this is very common to human interactions, such as conversations, when one party asks a question and second answers it, it’s still not a pattern that simply dominates. In daily lives, we experience countless cases of asynchronous interaction pattern.
For example, I may be working at my computer and I may ask my roommate to boil water and make a tea. Do I have a guarantee that she will do it? Of course not. If I ask her to go to Mother’s Market and buy milk, but she may go or may not go, and even if she would end up going, she might do a dozen other things before it. Another example of this is writing a letter (well, if you still remember what a real letter is). You write it, put in an envelop, address and stamp it, and depending on where it is mailed, you may wait for days, weeks, or maybe months, if to ever receive a response.
Thinking on the architecture of the system we are building, it helps to train your mind to think of these processes as natural phenomena, and think of data passing between different purpose nodes as events, that may or may not occur, and in case these events / messages are sent, to abstract from the reason why they were sent. In reality, it is possible that there may be more than one reason a node can emit an event, but for the receiving end it may not matter if event was generated automatically, was caused by a user or was “requested” my another part of system. The addressee of these events may be completely indifferent to the reason, and be only interested in the message payload.
Implications for the above is particularly important when working on the user-facing client side of the system. For example, animated spinning processing indicators for XHR actions may not make much sense if user’s action translate to a series of events that make a few hops before they reach intended consumer, which then may or may not send an event back.
I will not get into specifics of the project at this time, but for such use cases as described above we’re strongly considering “optimistic actions”, when upon user submission, we presume success of this action and modify UI accordingly, and only report to user if any of these actions resulted in error or were not complete, of which we may learn from the system later. This approach may be used only with great caution, depending on how critical is to notify user of actual result of their actions and guarantee that they were applied exactly as desired by user.
I will continue writing on this topic as we have more developments in this project.