Message Queues in System Design Interviews: When, Which and How
"I'll put a queue here" is a good instinct and a weak answer on its own. A queue changes your system's guarantees: work becomes asynchronous, failures become retries, and duplicates become your problem. Here's how to use one well in an interview.
When a queue earns its place
- Decoupling: the sender shouldn't wait for, or fail because of, a slow downstream service. Sending a message shouldn't depend on the notification service being up.
- Absorbing spikes: a flash sale produces 50x normal orders for ten minutes; the queue buffers them and workers drain it at a steady rate.
- Fan-out: one event ("order placed") feeds many consumers (email, analytics, inventory).
- Slow or retryable work: video transcoding, webhooks, report generation.
If the caller needs the result immediately to answer the user, a queue is usually the wrong tool.
Queues vs logs
| Message queue (e.g. SQS, RabbitMQ) | Log / stream (e.g. Kafka) | |
|---|---|---|
| Model | Messages are consumed and removed | Messages are appended and kept for a retention period |
| Consumers | Workers compete for messages | Each consumer group reads independently at its own offset |
| Replay | Generally no | Yes: rewind the offset and reprocess |
| Ordering | Limited (FIFO variants exist) | Guaranteed within a partition |
| Best for | Task distribution, background jobs | Event streams, many independent consumers, reprocessing |
Rule of thumb for interviews: jobs → queue; events many systems care about → log.
Delivery guarantees
- At most once: a message may be lost, never duplicated. Fine for metrics you can afford to drop.
- At least once: never lost, may be delivered more than once. The default in practice.
- Exactly once: true end-to-end exactly-once is hard. What systems actually offer is effectively once: at-least-once delivery plus idempotent processing.
So the real interview answer is: "Delivery is at least once, so consumers are idempotent."
Idempotent consumers
A consumer is idempotent if processing the same message twice has the same effect as processing it once. Common techniques:
- Give every message a unique ID and record processed IDs (with the side effect, in the same transaction where possible).
- Use natural idempotency: "set status to SHIPPED" instead of "increment shipped count".
- Use the downstream service's idempotency keys (payment APIs commonly support them).
Ordering
Global ordering doesn't scale. Instead, order per key: in Kafka, messages with the same key go to the same partition and are read in order. Choose the key that matters, such as orderId so all events for one order arrive in order. Different orders can then be processed in parallel.
Retries, backoff and dead-letter queues
- Retry failed messages with exponential backoff and jitter so a struggling dependency isn't hammered.
- After N failures, move the message to a dead-letter queue (DLQ) for inspection instead of blocking the rest.
- Alert on DLQ growth; a silent DLQ is lost work.
In SQS-style queues, a message being processed is hidden for a visibility timeout; if the worker doesn't delete it in time, it reappears for another worker. Set the timeout longer than the normal processing time, or extend it while work is running.
Backpressure
If producers outpace consumers, the queue grows. Watch queue depth and consumer lag, autoscale consumers on them, and decide what happens at the limit: reject new work, shed low-priority messages, or slow producers.
The outbox pattern
A classic bug: the service writes an order to its database, then publishes "order placed". If it crashes between the two, the order exists but no event is sent (or the reverse).
The transactional outbox fixes it: write the order and an outbox row in the same database transaction; a separate relay reads the outbox and publishes to the queue, marking rows as sent. Combined with idempotent consumers, nothing is lost and duplicates are harmless.
What interviewers push on
- "What if the consumer crashes halfway?" (At-least-once redelivery; idempotency.)
- "How do you keep events for one user in order?" (Partition by user ID.)
- "The downstream API is down for an hour." (Backoff, DLQ, and a queue sized for the backlog.)
- "How do you avoid publishing an event for a transaction that rolled back?" (Outbox.)
- "Kafka or SQS?" (Replay and multiple consumer groups → Kafka; simple job distribution → SQS.)
Practise it end to end. Prepd has questions on designing a message queue, webhook delivery and push notifications, each split into steps with written feedback on what an interviewer would push on.