Producers and consumers connected by a message queue

Message queues

When to go async, delivery guarantees, and the failure modes that surface first — poison messages, consumer lag, and exactly-once as a marketing term for effectively-once.

Why queues matter

A signup service once put welcome emails on a queue and called it done. A bad template crashed the consumer on every attempt, including retries. Nobody had wired a dead-letter queue, so the same poison message replayed forever, consumer lag climbed into the hundreds of thousands, and real signups sat unsent for hours before anyone noticed. The fix was a DLQ, an alert on DLQ depth, and a canary consumer that catches a bad template before it reaches production. Most queue design questions are this incident wearing a different costume.

Queues absorb spikes, decouple producers from slow consumers, and enable retries. Use them when the user doesn't need the side effect to finish inside the HTTP request. Misuse them and you add lag, ops complexity, and “where did my job go?” debugging.

Analogy diagram: wedding RSVP front desk with backstage caterer queue.
Wedding RSVP desk: guest gets instant yes; kitchen work runs on the queue.

That one incident contains most of the subject: messages that never finish, messages that finish twice, and a backlog nobody watches until the lag chart forces the issue. Design for those three from the start instead of bolting them on after an outage.

01When async

Side effects that can finish after the ack.

02Delivery

At-most / at-least / effectively-once.

03Idempotency

Duplicates are normal — design for them.

04Failures

Lag, poison, ordering, backpressure.

How to say this in the interview

When async comes up, open with a spoken default that covers delivery, idempotency, and poison handling. Customize the work item to the prompt:

That paragraph answers ~80% of queue questions before the broker brand comes up. Juniors name Kafka first; seniors name semantics and failure modes first, then pick a broker if asked. If they dig into Kafka vs SQS, you branch — you don't start there.

When async helps

If the user is staring at a spinner for work that doesn't change the immediate response, pull it off the critical path. Keep sync for paths where the user must know the outcome before continuing — payment authorization result, “was my seat reserved?”, login success. Queues shine when the acknowledgment can honestly mean “accepted for processing,” not “fully done.”

Sequence: producer sends a message, queue stores it, consumer receives and acknowledges, queue removes the message.
Queue buffer — send, store, dispatch, ack, remove.

Use the list below as prompts, not a mandate to queue everything. Each item is a classic side effect that can finish after the user already got a 200. The anti-pattern is queuing a 50ms DB update that the next screen needs — you paid broker latency and failure modes for nothing.

  • Notifications — email / push after signup.
  • Media pipeline — thumbnail after video upload.
  • Analytics — aggregate events without blocking the click.
  • Fan-out — timeline updates to millions of followers.
  • Settlement — payment settlement after order confirmation.
Interview takeaway

Queue work the user doesn't need finished inside the HTTP response — keep must-know outcomes synchronous.

Delivery semantics

Not all message delivery is equal. Know these three — interviewers will pick one and ask you to commit. The trap is treating “exactly-once” as a checkbox the broker magically provides. End-to-end exactly-once is usually a business-effect goal you build with idempotent writes, not a free feature.

Producer to queue to consumer with retry loop, dead-letter queue, and idempotency requirement.
At-least-once — retries until ack; poison messages go to DLQ; consumers must be idempotent.

At-most-once is fire-and-forget: simple, lossy, fine for some metrics. At-least-once retries until ack: duplicates happen, so consumers must be idempotent — this is the industry default. Exactly-once in the marketing sense usually means effectively-once at the side-effect layer: dedupe keys, transactional outbox, or idempotent upserts so processing twice doesn't charge twice.

  • At-most-once — fire and forget; messages may be lost.
  • At-least-once — retries until ack; consumers must be idempotent.
  • Exactly-once — usually “effectively once” via idempotent writes + dedupe keys.

If they push on Kafka transactions or SQS FIFO, say what those specifically buy — no duplicate produce within a transaction, or in-order delivery per group — and what they still don't guarantee once your code fans out to other systems.

Interview takeaway

Default to at-least-once delivery and make consumers idempotent — treat exactly-once as a business-effect goal, not a free broker feature.

Idempotency — the non-negotiable

The queue's job isn't speed — it's letting the slow part fail without taking the fast part down.

With at-least-once delivery, the same message can arrive twice. Your consumer must produce the same result whether it processes once or twice. This is non-negotiable for payments, inventory, and anything with side effects. If you only deepen one queue topic for interviews, deepen this one.

Practical patterns: store a message ID or business ID before side effects; prefer absolute writes (SET status = paid) over relative ones (ADD 50); gate emails and charges behind an “already processed” check. Also design for out-of-order delivery across partitions — global order is a luxury, not a default.

  • Dedupe key — store message ID or business ID in DB before side effects.
  • Natural idempotencySET balance = 100, not ADD 50.
  • Already-processed check — gate emails, charges, and status flips.
  • Out-of-order design — partitions can reorder across keys; don't assume global order.
Interview takeaway

Assume duplicates; gate side effects with a dedupe key or an inherently idempotent write.

What juniors get wrong vs what seniors say

Queue designs fail quietly until lag and duplicates show up in production. Beyond the poison-message incident above, these contrasts are what separate an answer that sounds rehearsed from one that sounds owned.

  • Junior — “We'll use exactly-once so duplicates can't happen.” Senior — “We'll use at-least-once and make the consumer idempotent — duplicates will happen; double charge won't.”
  • Junior — “Put it on Kafka and we're scalable.” Senior — “Kafka helps if we pick a partition key for order, size consumer groups for lag, and plan retention — the topic alone isn't a design.”
  • Junior — “Retry forever until it works.” Senior — “Retry with backoff, then DLQ — poison messages shouldn't block the partition forever.”
  • Junior — “Queues guarantee order.” Senior — “Order is per partition/key; cross-key order is a different product requirement and a different cost.”

A sizing example that shows you think in numbers: if producers emit 5k messages/sec and each consumer handles 500/sec, you need roughly 10 parallel consumers plus headroom, or lag grows without bound. Seniors size consumers against the produce rate; juniors draw one worker box and hope.

Interview takeaway

Juniors name the broker; seniors name delivery, idempotency, DLQ, and lag math — then pick a broker if asked.

Failure modes to mention

Queues hide failures until they become lag dashboards and angry support tickets. Name these four before the interviewer asks. A queue without metrics is a silent backlog — say that too.

  • Consumer lag — queue grows faster than workers process; add consumers or optimize the handler.
  • Poison messages — bad payload crashes the consumer; route to a dead-letter queue (DLQ).
  • Ordering — single partition preserves order; multiple partitions trade order for throughput.
  • Backpressure — slow consumers shouldn't OOM the broker; use pull-based limits and concurrency caps.

Also call out observability: lag metrics, DLQ depth, retry counts, and alert thresholds. If you only get one dashboard in the design, make it consumer lag with a page when it exceeds a product-defined SLO (for example, email lag > 5 minutes).

Interview takeaway

Mention lag, poison/DLQ, ordering vs partitions, and backpressure — that's the failure checklist interviewers expect.

The interview default

Use one crisp default answer, then branch only if they ask about Kafka vs SQS vs RabbitMQ. Broker choice is a follow-up; semantics are the main act.

When they name a broker, attach one distinctive property — retention and partitions for Kafka, managed ops and visibility timeout for SQS, routing flexibility for RabbitMQ — and tie it back to ordering, lag, and ops cost. Don't recite feature lists.

  • When they ask Kafka — log retention, consumer groups, partition key for order. → Full Kafka deep dive
  • When they ask SQS — managed ops, visibility timeout, DLQ; weaker native ordering.
  • When they ask RabbitMQ — flexible routing; classic for work queues.

That default covers ~80% of queue questions. Add broker specifics only when asked — and tie each to ordering, retention, and ops complexity.

Interview takeaway

At-least-once + idempotent consumers + backoff retries + DLQ is the safe default until the interviewer asks for a specific broker.

Cost and performance levers

Queue semantics clinic

Ordering vs throughput

Global order ⇒ single worker (low throughput). Per-key order ⇒ partition by key. No order ⇒ max parallelism. Say which your product needs before picking Kafka vs SQS.

Failure modes to mention

Call out at least one dependency failure (DB down, cache stampede, queue lag, region outage) and your mitigation (timeouts, retries with jitter, degraded mode, circuit breaker).

Live client fan-out often pairs queues with a push tier — see Real-time updates.

Interview Q&A by level

Practice saying these out loud for message queues. Interviewers grade clarity and judgment more than buzzwords.

Interview takeaway

Match depth to the bar: define → trade off → operate. Don't dump principal answers in an entry-level screen.

← Lattice