When you should NOT use messaging, or how not to make things harder for yourself

Every architectural decision, every framework, and every design pattern has its pros and cons. Today, we will dive into the world of asynchrony, and more specifically, when it is worth using messaging, and when it is better to let it go and not make things harder for yourself.

Messaging as a panacea? Not always

When we hear about problems with application scalability or responsiveness, many of us immediately think of asynchrony. Queues, message brokers, background jobs – they sound like magic spells that will solve all our problems. And yes, they often do. But just as often, they can introduce unnecessary complexity where we don't need it at all.

Imagine a typical scenario: the user clicks “Save”, and we need to update a few records in the database and send an email notification. The thought comes to mind: “Send the email asynchronously!” We put the task in a queue, give the user an immediate response, and a worker in the background will take care of the rest. Sounds great, right? But what if sending the email is so fast that adding all the overhead associated with the queue (serialization, deserialization, worker management, background error handling) takes longer and is more complicated than just doing it synchronously?

When the business flow is synchronous

The key issue is always business flow. If a business process is synchronous by nature, i.e., it requires an immediate response from the system to user action (input -> immediate output -> next input), trying to force it into an asynchronous framework will only complicate matters. Think of forms where you enter data and the system has to verify it immediately and show the result. Here, messaging, instead of helping, will become a hindrance.

  • Bad use case: You need immediate real-time validation of input data. Sending data to a queue and then waiting for a response from the worker will introduce unnecessary delay and greatly complicate client-side state handling.
  • Good use case: The user sends a large file for processing. They can wait a few seconds or even minutes before seeing the final result. Here, asynchrony is ideal – the user gets immediate feedback that “we are processing your file,” and the heavy lifting is done by the worker in the background.

The ordering challenge: the biggest pitfall

One of the biggest and often underestimated problems in asynchronous systems is message processing order (ordering). In a synchronous world, this is trivial – operations are performed in the order in which they were called. In an asynchronous system, messages sent in the order A, B, C may arrive and be processed as C, A, B.

To deal with ordering in asynchronous systems, we often have to build complex mechanisms: timestamps, version numbers, dedicated queues for specific entities, or even saga patterns. All of this adds complexity that must be taken into account in the cost-benefit analysis. If ordering is critical to your business, think twice about whether asynchrony is the right way to go.

Think twice

Before you decide on messaging, ask yourself:

  1. Does the business flow really need asynchrony? Can the user wait? Can the system operate without an immediate response?
  2. Is the order of message processing critical? If so, how am I going to ensure it, and what are the costs (technical and maintenance) involved?
  3. What is the actual scale of the problem I am trying to solve? Won't the overhead associated with introducing a message broker outweigh the benefits?

Don't get me wrong. Messaging is a powerful tool, irreplaceable in many scenarios, such as building distributed systems, data aggregation, streaming processing, or offloading main processes. But like any powerful tool, it requires conscious and thoughtful use. Sometimes a simple, synchronous solution is simply the best. Remember, we build architecture for business. Sometimes simplicity and synchrony are our best allies.

Happy simplifying!