Messaging patterns comparison

Message Queue vs Pub/Sub

Two fundamental patterns for asynchronous communication between services. Getting this distinction right is the first step to designing a reliable event-driven architecture — and it comes up in virtually every microservices system design discussion.

TL;DR

  • Message Queue — point-to-point. One producer, one consumer processes each message. Message is removed once acknowledged. Best for task distribution and work queues. Examples: RabbitMQ (default queue), SQS.
  • Pub/Sub — broadcast. One publisher, many subscribers each receive a copy. Best for event notification and fan-out. Examples: Kafka topics, SNS, Google Pub/Sub.
  • They complement each other. The SNS → SQS pattern (fan-out to isolated queues) is one of the most common patterns in AWS-based microservices.

Side-by-side comparison

AspectMessage QueuePub/Sub
Delivery modelPoint-to-point: one consumer per messageBroadcast: all subscribers receive a copy
OrderingTypically FIFO within a queueNot guaranteed across subscribers
PersistenceMessage waits until consumed (durable)Varies — Redis drops, Kafka retains by offset
Fan-outOne message → one consumerOne message → all subscribers
CouplingProducer knows there is a queuePublisher knows nothing about subscribers
Load balancingNatural — competing consumers share workEach subscriber does all the work independently
Use caseTask queues, email sending, job workersEvent streaming, notifications, audit logs
ExamplesRabbitMQ, Amazon SQS, CeleryApache Kafka, Amazon SNS, Google Pub/Sub, Redis Pub/Sub

Code side-by-side

Publishing an order event and consuming it:

Message Queue (SQS — one worker)

// Producer: send task to queue
await sqs.sendMessage({
  QueueUrl: ORDER_QUEUE_URL,
  MessageBody: JSON.stringify({
    orderId: 'ord_123',
    action: 'send_confirmation_email',
  }),
}).promise();

// Consumer: exactly one worker picks this up
// Other workers get different messages

Pub/Sub (SNS → multiple SQS queues)

// Publisher: broadcast event
await sns.publish({
  TopicArn: ORDER_EVENTS_TOPIC,
  Message: JSON.stringify({
    orderId: 'ord_123',
    event: 'order_placed',
  }),
}).promise();

// SNS fans out to:
// → SQS queue for email service
// → SQS queue for fulfilment service
// → SQS queue for analytics

When to use a Message Queue

  • Task distribution. You have a pool of workers and want to share work evenly — image resizing, email sending, PDF generation.
  • Exactly-once processing required. Each job must be done by exactly one worker; duplicate processing would cause errors (double charge, double send).
  • Rate limiting downstream services. Queue depth acts as a natural buffer; consumers process at a sustainable rate even when producers burst.
  • Retry with backoff. Failed messages go to a dead-letter queue for inspection and reprocessing without blocking healthy messages.

When to use Pub/Sub

  • Event notification to multiple services. A user signs up → auth service, email service, analytics service, and onboarding service all need to know.
  • Audit logging and observability. Every service publishes events; a central subscriber records them all without the publishers knowing.
  • Decoupling producers from consumers. New subscribers can be added without changing the publisher — true open/closed principle at the architecture level.
  • Event sourcing and stream processing. Kafka topics retain all events; consumers replay history to rebuild state or run analytics.

English phrases engineers use

Message Queue conversations

  • "Messages pile up in the queue when consumers are slow."
  • "Failed messages go to the dead-letter queue (DLQ)."
  • "We have competing consumers — 5 workers share the same queue."
  • "The consumer must acknowledge the message or it's requeued."
  • "We set a visibility timeout to prevent duplicate processing."

Pub/Sub conversations

  • "We publish an event to the topic; subscribers react independently."
  • "The email service subscribes to the user-signup topic."
  • "We do fan-out via SNS → multiple SQS queues."
  • "Kafka consumers track their offset and can replay old events."
  • "The publisher is decoupled — it doesn't know who's listening."

Quick decision tree

  • One consumer should process each message → Message Queue
  • Multiple independent services need the same event → Pub/Sub
  • Task distribution among worker pool → Message Queue
  • Audit log / event stream → Pub/Sub (Kafka)
  • Need replay of past events → Pub/Sub (Kafka)
  • Need dead-letter queue and retry → Message Queue (SQS/RabbitMQ)
  • Need both broadcast AND isolated processing → SNS + SQS fan-out

Common Mistakes and Trade-offs

A pervasive mistake developers make when initially evaluating message queues versus pub/sub systems is treating them as interchangeable solutions for asynchronous communication. Many teams assume that adding a queue will magically solve all their decoupling problems, overlooking the fundamental differences in delivery guarantees and routing capabilities. This often manifests as using a simple FIFO queue to handle complex event-driven workflows where exactly-once semantics are critical; this can lead to significant data corruption or duplicated processing if not carefully managed with idempotent consumers and robust error handling – something that's far easier to achieve with a system designed for publish/subscribe from the ground up. It's easy to fall into the trap of thinking 'queue = decoupling,' but true decoupling requires thoughtful design regardless of the technology.

At scale, a significant trade-off emerges involving message ordering and eventual consistency that's frequently underestimated with traditional message queues. While guarantees about strict ordering are often advertised, maintaining this across a large cluster – especially when consumers operate independently and potentially concurrently – introduces substantial operational complexity. Pub/Sub systems, particularly those built on distributed log platforms, inherently handle ordering within a consumer's view, but at the cost of eventual consistency for other consumers. This means that even if you meticulously configure your queue for serial delivery, you're still battling against potential out-of-order messages in downstream processing; the operational overhead to manage this is substantially higher than simply accepting eventual consistency and its associated complexity.

The misconception that pub/sub systems are 'always better' for event-driven architectures ignores the critical role of consumer design. A poorly designed consumer, regardless of the underlying technology – whether it's a message queue or pub/sub – can quickly become a bottleneck. If consumers aren't optimized to handle bursts of traffic, or if they lack proper rate limiting and backpressure mechanisms, the entire system can grind to a halt. Furthermore, the flexibility of pub/sub systems – while powerful – demands more rigorous monitoring and tracing; without these, it's extremely difficult to identify performance issues or diagnose failures. The ease of adoption shouldn't overshadow the need for careful architectural planning and robust consumer development.

Migrating from a message queue-centric architecture to a pub/sub model (or vice versa) isn't simply a matter of swapping components; it often necessitates a fundamental shift in thinking about data ownership and responsibility. Consider scenarios where multiple teams rely on the same event stream – with queues, this frequently leads to tightly coupled systems and complex coordination mechanisms. Pub/sub promotes a more decentralized approach, but requires careful governance around schema evolution and versioning. Combining elements of both approaches—using message queues for short-lived, transactional tasks within a pub/sub ecosystem—can be an effective strategy, allowing you to leverage the strengths of each technology while mitigating their respective weaknesses. However, this hybrid model introduces its own complexities regarding routing, transformation, and overall system management.

Frequently asked questions

What is a message queue in plain English?

A message queue is a holding area for messages sent between services, with a point-to-point delivery model: one producer sends a message, and exactly one consumer processes it. Once consumed and acknowledged, the message is gone. Think of a task queue — one worker picks up each job. Examples: RabbitMQ, Amazon SQS.

What is the Pub/Sub pattern?

Publish/Subscribe (Pub/Sub) decouples producers (publishers) from consumers (subscribers) through topics. When a publisher sends a message to a topic, every subscriber to that topic receives a copy. One event, many receivers. Examples: Google Pub/Sub, Amazon SNS, Kafka topics, Redis Pub/Sub.

Can a message queue fan out to multiple consumers?

A single queue is point-to-point. To fan out with a queue-based system (like SQS), you combine it with a Pub/Sub layer — SNS broadcasts to multiple SQS queues, each with its own consumer. RabbitMQ achieves this with exchange types (fanout, topic).