Almost every candidate reaches for a message queue at some point in a system design interview, and almost every one of them draws the same box. The box is not the problem. The problem is that the interviewer's next four questions are about what happens after the box, and reading a definition of a message queue prepares you for none of them.
This guide covers what interviewers actually probe, in the order they usually probe it.
The only reason to add one
A message queue lets one service hand work to another asynchronously. The producer enqueues in milliseconds and moves on; the consumer drains at its own pace.
That single property — the producer does not wait — is the entire justification, and it buys you three things:
- Burst absorption. A traffic spike grows the queue instead of timing out user requests. The web tier stays responsive while the slow work backs up.
- Fault isolation. If the consumer fleet dies, messages wait. Nothing is lost and nothing upstream fails.
- Independent scaling. Producers and consumers scale on different signals, because they are no longer coupled by a synchronous call.
The corollary matters more than the list: a queue does not make the work faster. It moves the work out of the request path. If the total processing capacity is insufficient, a queue converts a fast, visible failure into a slow, invisible backlog — which is worse, because nobody gets paged for a queue that is merely growing.
Question 1: "Why not just call the service directly?"
This is the warm-up, and the wrong answer is "because queues scale better."
The right answer names the coupling you are removing. A synchronous call means your availability is the product of every downstream availability, and your latency is the sum of every downstream latency. If sending a welcome email is a synchronous step in signup, then the email provider's bad afternoon becomes your signup outage.
Then volunteer the cost, because the interviewer is about to ask anyway: you have traded a simple failure mode for a harder one. Synchronously, a failure is immediate and the caller knows. Asynchronously, the caller gets a success and the work fails somewhere else, later, where nobody is watching unless you built the watching.
Question 2: "A consumer crashes halfway through. What happens?"
This is the real question, and it is where most candidates stop being specific.
The message was never acknowledged. After a visibility timeout, the broker redelivers it to another consumer. That is correct behaviour, not a bug — and it is why the default delivery guarantee is at-least-once: the work may run twice.
So the consumer must be idempotent. In practice that means one of:
- A natural idempotency key.
SET user.status = 'active'is safe to repeat.INCREMENT balance BY 10is not. - A dedup table. Record the message id inside the same transaction as the side effect. A replay finds the id and returns early.
- A conditional write.
UPDATE ... WHERE version = nfails harmlessly the second time.
The detail that signals real experience: the dedup record and the side effect must commit together. If you write the side effect, then record the id, a crash between them replays the side effect forever.
Question 3: "How do you get exactly-once delivery?"
You do not, and saying so plainly is the strongest available answer.
Exactly-once delivery across a network is not achievable in the general case — the two generals problem has not gone anywhere. What real systems provide is at-least-once delivery plus an idempotent consumer, which produces exactly-once processing. The distinction is not pedantry; it tells the interviewer you know where the guarantee actually lives, which is in your consumer, not in the broker.
Some brokers offer transactional semantics inside their own boundary — Kafka can do this producer-to-topic-to-consumer-offset. That is real and useful. But the moment a side effect leaves that boundary, such as charging a card or calling a third-party API, you are back to needing a dedup key. Interviewers like this question precisely because the marketing on this topic is misleading.
Question 4: "The queue is backing up. What do you do?"
The question separates people who have operated a queue from people who have read about one. Work through it in this order:
- Is arrival up or processing down? They have completely different fixes, and the metric that answers it is consumer lag over time, not queue depth.
- Is it uniform or skewed? In a partitioned log, aggregate lag hides a single hot partition. One tenant keyed onto one partition can stall while everything else looks healthy.
- Is something poisoned? A message that fails deterministically and gets redelivered forever will block an ordered partition indefinitely. This is what a dead-letter queue is for: after N attempts, move it aside so the rest can drain, and alert on the DLQ depth — a silent DLQ is a bug factory.
- Then scale. Add consumers, but only if the downstream can take it. Adding consumers that all hammer a saturated database converts a queue backlog into a database outage.
If arrival genuinely exceeds any reasonable processing capacity, you need backpressure: reject or shed at the edge, with rate limiting on the producer side. An unbounded queue is not a solution to overload; it is a decision to fail later and less visibly.
Question 5: "Queue or log — which, and why?"
Interviewers increasingly ask this because it reveals whether you understand the two models rather than one product.
A traditional queue (SQS, RabbitMQ) deletes a message once acknowledged. Work is distributed across consumers, ordering is best-effort, and scaling consumers is trivial because no consumer owns anything. It suits task distribution.
A log (Kafka, Kinesis, Pulsar) keeps messages for a retention window and tracks a per-consumer offset. Multiple independent consumer groups read the same stream, ordering holds within a partition, and you can replay history. It suits event streaming, change data capture, and any case where more than one system needs the same events.
The practical decision rule: if two different teams will want these events next year, pick the log. Retention and replay are the features you cannot retrofit. The cost is that partitions are your unit of parallelism and ordering at once, so the partition key becomes a design decision with real consequences — and a poor key produces exactly the hot-partition problem in question 4.
Question 6: "Does ordering matter here?"
Usually the honest answer is "only within a key," and that answer is worth a lot.
Global ordering across a distributed queue costs you parallelism, because it forces a single consumer. Per-key ordering — all events for one user, one account, one document — is almost always what the business actually requires, and it is achievable by partitioning on that key.
Say which key, and say what happens when a key is hot. That one sentence covers ordering, partitioning and the failure mode together.
What a strong answer sounds like
Weak, and extremely common:
"I'd put Kafka between the API and the workers so it scales."
Strong:
"The transcode step takes about thirty seconds and the user does not need to wait for it, so I'd enqueue a job and return immediately. That means at-least-once delivery, so the worker keys on the job id and skips work it has already done. Partition by video id so one user's uploads process in order. If lag grows I check per-partition lag first — a single hot key is more likely than a global capacity problem — and there's a DLQ after five attempts so one bad file can't block the partition."
Same architecture. The difference is that the second answer has clearly been operated rather than only drawn.
Capacity numbers you should be able to produce
Design rounds are decided on estimates as much as on architecture. For a queue, know how to reach:
- Throughput: messages per second at peak — typically daily volume divided by 86,400, times a peak factor of three to five.
- Storage: for a log, retention days times daily volume times message size times replication factor. This number surprises people.
- Consumer count: peak throughput divided by per-consumer throughput, with headroom to drain a backlog rather than merely keep up. A fleet sized exactly to arrival rate never recovers from an incident.
The back-of-the-envelope calculator does this arithmetic if you want to check yourself, and the latency numbers reference is worth internalising before any design round.
Practice these
Reading about queues is not the same as being asked about them with follow-ups:
- Design a Distributed Message Queue — build the thing itself: partitioning, replication, delivery guarantees
- Design a Notification System — the classic fan-out case, where queues are the whole answer
- Design a Web Crawler — frontier queues, politeness, and dedup at scale
- Design a Payment Processing System — where idempotency stops being theoretical
Related concepts: idempotency, backpressure, change data capture, circuit breaker.
Frequently asked questions
Is a message queue always the right answer for slow work? No. If the work must complete before the user can proceed, a queue only adds latency and complexity. Queues are for work the caller does not need to wait for.
What is the difference between a message queue and a message broker? The queue is the data structure; the broker is the service that hosts queues or topics, handles delivery, and tracks acknowledgements. In interviews the terms are used loosely and nobody will penalise you for it.
Should I name a specific technology in an interview? Name one, then justify it by property rather than brand — "a log like Kafka, because two other teams will want these events." Naming a product without a reason is the single most common weak answer on this topic.
How many partitions should a topic have? Enough that peak throughput divided by per-partition throughput leaves headroom, and at least as many as your maximum consumer count, since a partition cannot be shared within a consumer group. Over-partitioning costs metadata overhead and rebalance time, so it is a trade rather than a free parameter.
What is a dead-letter queue? A separate queue for messages that have failed processing repeatedly. It stops a single poison message from blocking a partition forever, and its depth should be alerted on — a dead-letter queue nobody reads is just deferred data loss.