Skip to content

cd ../tech-notes

NOTE

The hardest part of sending thousands of WhatsApp messages isn’t sending them

by 2 min read

cat whatsapp-webhooks-queues-backpressure.md en

It’s surviving everything that comes back.

I learned this while working on a messaging system built around the official WhatsApp Business Platform.

Sending a request to an API is relatively simple.

But once the system starts operating at volume, every message creates more work:

  • delivery updates
  • read receipts
  • failures
  • incoming messages
  • retries
  • database writes
  • other events arriving through webhooks

At that point, a webhook can easily become a bottleneck.

I like to think of it as a hotel reception.

Imagine the receptionist checking in a guest, carrying the luggage upstairs, cleaning the room and preparing breakfast before talking to the next person in line.

The problem isn’t that the receptionist is slow.

The problem is that the reception is doing work that belongs somewhere else.

That changed the way I designed the flow.

Instead of making the webhook process everything synchronously, its responsibility became much smaller:

Receive → Validate → Enqueue → Acknowledge

The heavier work happens asynchronously through workers, with Redis/BullMQ controlling the queue and MongoDB storing the application state.

That separation matters more than it may seem.

A queue doesn’t magically make processing faster.

It separates the rate at which work arrives from the rate at which your system can safely process it.

If 5,000 events arrive in a burst, I don’t want 5,000 HTTP requests competing to perform database operations at the same time.

I want the ingress layer to remain responsive while workers consume the backlog at a rate the rest of the system can sustain.

And that leads to another important lesson:

Once processing becomes asynchronous, you have to think differently about failure.

  • What happens if a worker crashes after writing to the database?
  • What if an event is processed twice?
  • What if the external API retries something you have already handled?
  • What happens when workers are healthy but MongoDB becomes the bottleneck?

That is where concepts like idempotency, backpressure, retry strategy, concurrency limits and observability stop being theoretical.

They become part of keeping the system alive.

The biggest lesson for me was this:

Scalability is not just about processing more work. It is about controlling when, where and how much work happens at once.

Sometimes the best architectural decision is not making a worker faster.

It is making sure the rest of the system never has to move at the same speed as the incoming traffic.

MORE NOTES

HIRING? GET THE FULL CV

filipe --send-cv --to=<email>

Leave your email and I will send you the full CV as a PDF, with direct contact details.

Used only to send the CV and reply to you. No newsletter.