System Design's own message-queue lesson covered one shape — one producer, one logical consumer, each message handled exactly once by whoever picks it up first. Real messaging systems also support a second, distinct shape, and picking the wrong one for the job is a design mistake that surfaces much later, expensively.
3 min read
Every messaging system ultimately answers one of two different questions: "who should handle this specific piece of work" or "who else needs to know this happened." These are genuinely different problems, and the two core messaging shapes exist specifically to answer each one correctly.
A queue holds messages representing units of work — "resize this image," "charge this invoice" — where each message should be processed exactly once, by whichever available worker picks it up. Multiple workers can consume from the same queue to share the load, but a given message only ever goes to one of them. This is exactly the shape System Design's own queue lesson covered: a producer publishes, a pool of consumers divides up the work.
A topic (or "exchange" in some systems) broadcasts each message to every subscriber independently — not to whichever one happens to be free. An OrderPlaced event published to a topic might need to reach an email service (send a confirmation), an analytics service (record a metric), and a fraud-check service (run a risk score) — three entirely independent consumers, each processing the same message for its own unrelated purpose. A queue's "first one to grab it wins" semantics would be actively wrong here: only one of the three would ever see any given message.
Using a queue when the real need is pub/sub is an easy, costly mistake: a team adds a second consumer expecting it to also see every message, and instead half the messages silently go to the first consumer and half to the second — each gets an incomplete stream with no error, no crash, nothing that looks broken in testing with a single consumer. The reverse mistake — using pub/sub for work that should only happen once — causes visible duplicate processing (three services all trying to "charge this invoice" because the broadcast model delivered it to all three).
| Queue (point-to-point) | Topic (pub/sub) | |
|---|---|---|
| Each message delivered to | One consumer | Every subscriber |
| Right for | Work to be done once | Facts other systems should know about |
| Adding a consumer | Splits existing load across more workers | Adds a new, independent recipient of every message |
| Real examples | A job queue (SQS, a Celery task queue) | An event bus (SNS, Kafka topics, RabbitMQ fanout exchange) |
Kafka (covered in depth next lesson) technically implements pub/sub at the topic level — every consumer group sees every message — but supports queue-like load-sharing within a single consumer group, where each message goes to only one member of that group. This isn't a contradiction: it's pub/sub across groups, queue-like within a group, and understanding which layer you're reasoning about is exactly the distinction this lesson exists to make explicit before the next several lessons build on it.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. In a point-to-point queue, how many consumers does a given message get delivered to?
2. What silent failure mode happens if a team uses a queue when pub/sub was actually needed?
3. How does Kafka blur the line between queue and pub/sub semantics?
Message Queues & Event-Driven Architecture