All questions
#12Messaging & Real-timeMedium

Design a Notification System

Fan out push, email and SMS reliably without spamming anyone.

Amazon logo
Uber logo
Doordash logo
Meta logo

Asked at Amazon, Uber and 3 more

2 min read·7 sections

A notification system sends messages to users over push (iOS, Android, web), email and SMS: "Your order shipped", "Your driver is here", "New login from Chrome". Many teams send through it, so it must be reliable, respect user preferences, and absorb huge spikes such as a marketing campaign to 50 million users.

Requirements#

Functional

  • Send through push, email and SMS, using providers like APNs, FCM, SES and Twilio.
  • Transactional notifications (receipts, alerts) and bulk campaigns.
  • Users can opt out per channel and per category, and set quiet hours.
  • Templates with variables and localisation.

Non-functional

  • Transactional notifications go out within seconds.
  • At-least-once delivery with no duplicate sends.
  • Spikes of millions of notifications per minute.

High-level design#

Order service
Notification APIvalidate + dedupe
Preference checkopt-outs, quiet hours
Preference check
Push queue
Push workers
APNs / FCM
Preference check
Email queue
Email workers
Email provider
Preference check
SMS queue
SMS workers
SMS provider
Push workers
Notification logstatus per message
Requests are validated once, then queued per channel so one slow provider can't block the rest
  • Notification API: accepts { userId, type, data } from internal services, renders the template and assigns a notification ID.
  • Preferences: a fast lookup (cached) of each user's opt-outs, channels and time zone.
  • Per-channel queues: separate queues mean a slow SMS provider never delays push notifications.
  • Workers: call the third-party provider, handle its errors and rate limits, and record the result.

Reliability#

  • Idempotency: callers pass an idempotency key (such as order-123-shipped). The API ignores repeats, so retries upstream don't produce duplicate texts.
  • Retries: if a provider times out, retry with exponential backoff and jitter. After a few attempts move the message to a dead letter queue for inspection.
  • Durable queues: Kafka or SQS persist messages, so a crashed worker doesn't lose them.
  • Provider failover: keep a second email or SMS provider and switch when the first one's error rate spikes.

Respecting users#

  • Check opt-outs at send time, not when the request arrived, in case the user unsubscribed in between.
  • Hold non-urgent notifications during quiet hours in the user's local time.
  • Rate limit per user, for example at most 3 marketing pushes a day, to avoid spam and app uninstalls.
  • Group bursts ("You have 5 new comments") instead of five separate pushes.

Device tokens#

Push needs a device token per app install. Store tokens per user, refresh them on every app launch, and delete tokens when APNs or FCM reports them invalid. Stale tokens waste quota and hurt delivery rates.

Bulk campaigns#

Don't enqueue 50 million messages in one shot. A campaign scheduler pages through the audience and feeds the queues at a controlled rate, which stays inside provider limits and keeps transactional traffic flowing.

Tracking#

Log every state change, queued → sent → delivered → opened, from provider callbacks. This powers analytics, debugging ("why didn't I get my code?") and automatic suppression of addresses that bounce.

Finished this walkthrough?

Mark it as read to track your progress in the list.