01 : Home 02 : About 03 : Resume 04 : Blog
Back to Blog

Putting RabbitMQ in Production: What Surprised Me

Lessons from implementing event-driven architecture with RabbitMQ for business applications at Linovt.

At Linovt, I worked on a system that needed to handle asynchronous communication between services: a client messaging application, workshop management workflows, and various background processes. We chose RabbitMQ as the message broker.

Here is what I learned from putting it in production.

Why Event-Driven Made Sense

The system had a clear pattern: certain actions needed to trigger multiple downstream processes, but none of them needed to happen synchronously. When a workshop was created, we needed to send notifications, update dashboards, and log the event. Doing all of that in a single HTTP request-response cycle would have been brittle and slow.

Switching to an event-driven model meant each service could handle its own concerns independently. The workshop service published an event. Other services subscribed to what they cared about. If one service was down, the others kept working.

What RabbitMQ Actually Does Well

Message durability is reliable. We configured queues with persistent messages and mirrored queues for high availability. RabbitMQ handled broker restarts without losing messages. This was not a feature we noticed until we needed it, and then it was critical.

Dead letter queues save you. Messages that cannot be processed go to a dead letter queue instead of being lost. We used this to debug integration issues by inspecting failed messages. It also let us implement retry logic without cluttering the main queue.

Routing with topic exchanges is flexible. We used topic exchanges so subscribers could filter messages by routing key patterns. For example, workshop.*.created matched any workshop creation event regardless of type. This pattern made adding new subscribers trivial.

What Caught Me Off Guard

Monitoring is not optional. RabbitMQ works well until it does not. We had a production incident where a queue backlog grew to hundreds of thousands of messages because a consumer crashed silently. Without alerting on queue depth, we would not have noticed until users complained. Now I consider monitoring infrastructure to be part of the application.

Message ordering is not guaranteed. RabbitMQ preserves order within a single queue under normal conditions. But with multiple consumers, retries, and dead lettering, messages can arrive out of order. If your system depends on ordering, you need sequence numbers or idempotent consumers.

Serialization format matters. We started with JSON. It was human-readable and easy to debug. But as message volumes grew, the serialization overhead became noticeable. For high-throughput paths, we switched to a more compact format. JSON was fine for most cases, but understanding the tradeoff early would have saved us a migration.

What I Would Tell Someone Starting

  1. Use dead letter queues from day one. Adding them later is a migration. They cost nothing to configure upfront.
  2. Monitor queue depth and consumer lag. These two metrics will tell you more about system health than anything else.
  3. Idempotent consumers are worth the effort. If a message is processed twice, your system should handle it gracefully. Network failures happen. Design for that reality.
  4. Start with a simple exchange topology. You can always add complexity later. Do not design a routing graph that looks like a subway map on day one.

Event-driven architecture is not the right choice for every system. But when the problem involves asynchronous coordination between services, it is hard to beat. RabbitMQ is mature enough to trust and simple enough to operate.

Just remember to monitor the queue depth.