Why put a queue in between
In a busy restaurant, the waiter doesn’t stand in the kitchen until your food is ready. They pin your order ticket on a rail and go back to the tables. Cooks take tickets from the rail in order, at their own speed. If ten orders arrive at once, the rail just gets longer for a while; nobody is turned away, and the waiter stays fast.
A message queue is that ticket rail. A producer (the checkout service) puts a message on the queue (“order 1001 was placed”) and moves on. One or more consumers (the email service, the warehouse service) take messages off and do the work.
Queues decouple time (work happens later), rate (producers and consumers run at different speeds; the queue absorbs the difference), availability (consumers can be down; messages wait) and knowledge (producers publish facts; any number of consumers subscribe). Two families: work queues (SQS, RabbitMQ: each message handled by one consumer, deleted after ack) and logs (Kafka, Kinesis, Pub/Sub: an append-only, partitioned, retained stream that many consumer groups read independently, replayable). The costs are eventual consistency, duplicate delivery and operational complexity.
Checkout with a queue
Tap a box to see what it does.
Mobile app
Gets "Thank you, order 1001" as soon as the order is saved and paid, in a few hundred milliseconds.
Checkout service
Does only what must happen before answering: save the order, charge the card. Then publishes an OrderPlaced event and returns.
Queue / topic
Holds events until every subscriber has handled them. If a consumer is down, events wait for it.
Email service
Sends the confirmation. If the email provider is slow, only this queue backs up; checkout is unaffected.
Warehouse service
Reserves stock and schedules picking. Owned by another team; it just subscribes to the event.
Invoice worker
Generates the PDF (slow, CPU-heavy) and scales independently from web servers.
What the queue gives you:
- Fast responses: the user waits only for the steps that must happen now.
- Spike absorption: if orders arrive faster than emails can be sent, the queue grows for a while and shrinks later; nothing is lost.
- Isolation: the email provider being down delays emails, not orders.
- Independent teams: adding “send a push notification on every order” means a new consumer; checkout doesn’t change.
The price: the work isn’t done when the user sees “thank you”. The email arrives a few seconds later, and your code must handle that “not yet” state.
Predict the output
During a sale, orders arrive at 1,000 per second for 60 seconds. The email consumers can send 400 emails per second. How many emails are waiting when the spike ends, and how long until the queue is empty if orders then drop to 100 per second?
For 60 seconds the queue grows by 1,000 − 400 = 600 messages per second: 36,000 waiting. Afterwards it shrinks by 400 − 100 = 300 per second: 36,000 ÷ 300 = 120 seconds, about 2 minutes. The last customers get their email two minutes late, and nobody's checkout failed. Watching this backlog (consumer lag) and adding consumers when it grows is how queue-based systems are scaled.
Now or later?
For a ride-hailing app’s “end trip” action, decide which steps must finish before the driver’s app shows “Trip complete”, and which can go through a queue:
- Mark the trip as completed
- Calculate the fare
- Charge the rider’s card
- Email the receipt
- Update the driver’s weekly earnings dashboard
- Ask the rider for a rating (push notification)
- Update the city heat map
›
For each step ask: does the user need its result on the next screen, and does anything else depend on it happening first?
SolutionTry it yourself first
Synchronous (before responding): 1 (the trip must be finished, or the driver could be assigned another ride with this one open) and 2 (the next screen shows the fare). 3, charging, is often done asynchronously too, with the trip in a “payment pending” state and retries, because a slow payment provider shouldn’t block the driver; but the fare and the obligation to pay are recorded synchronously.
Through a queue: 4, 5, 6 and 7. None changes what the driver sees next, each can fail and retry on its own, and the heat map and dashboards are happy with data a few seconds old.
The general rule: do synchronously what the user must see or what later steps depend on; publish an event for everything else.
Queue, pub/sub, or log?
| Work queue (SQS, RabbitMQ) | Log (Kafka, Kinesis) | |
|---|---|---|
| Delivery | Each message to one consumer, deleted on ack | Retained for days; each consumer group tracks its own offset |
| Fan-out | Via exchanges/topics to separate queues | Any number of groups read the same data |
| Ordering | Best effort (FIFO queues: per group, limited throughput) | Per partition, strict |
| Replay | No (once acked, gone) | Yes, by resetting offsets |
| Scaling consumers | Add consumers freely | Parallelism capped by partition count |
| Typical use | Jobs, tasks, commands | Event streams, CDC, analytics, event sourcing |
- Queues decouple time, rate, availability and ownership; the costs are eventual consistency, duplicates and operational load.
- Work queues (SQS, RabbitMQ): per-message ack/delete, competing consumers, no replay. Logs (Kafka): partitioned, retained, replayable, per-group offsets, parallelism bounded by partitions.
- Size by arrival vs service rate; monitor backlog/lag as the scaling signal.
Check this part
Why send the confirmation email through a queue instead of during checkout?
Orders arrive faster than consumers can process them for a minute. What happens?
You need several independent consumers to read the same events and replay them after a bug fix. Which fits?