System design · must-tier · asked at Amazon, Apple, Google, Meta, Microsoft, Netflix

Design a Notification System

mediumhldqueuefanoutdelivery

Prompt

"Design a notification service. Internal product teams call it to send push, email, and SMS to users. It has to handle a burst where one event notifies millions of people, and it must not send the same notification twice."

Clarifying questions

  • Who are the callers? Internal services, via an API. They pass a user id or a list of user ids, a template, and parameters. No end-user-facing composition UI.
  • Channels? Mobile push (via the platform push services), email, and SMS. In-app inbox is a follow-up.
  • Ordering guarantees? Per user, notifications from one source should arrive in order when practical; no global ordering.
  • Delivery guarantee? At-least-once to the external provider, with deduplication so users do not see duplicates. Some loss under extreme failure is tolerable for low-priority notifications, not for transactional ones (a security alert must arrive).
  • User preferences? Yes: opt-outs per channel and category, quiet hours, and rate caps per user per day.
  • Scale? Hundreds of millions of users; steady state of tens of thousands of notifications per second with bursts of tens of millions in a few minutes.
  • Latency? Transactional notifications within seconds; marketing or digest notifications can take minutes.
  • Do we track delivery and opens? Delivery status yes; opens for email and push where the provider reports them.

Requirements

Functional

  • Accept a notification request for one or many users; render a template per user and channel; deliver via the right provider.
  • Respect user preferences, quiet hours, and per-user caps.
  • Deduplicate: a retried request or a retried internal step must not produce a second delivery.
  • Track status per notification and expose it to callers.
  • Prioritise transactional over bulk traffic.

Non-functional

  • High availability for accepting requests; the API should ack quickly and process asynchronously.
  • Burst absorption: a spike of 10M+ notifications should be enqueued in seconds and drained at provider-limited rates without dropping steady-state transactional traffic.
  • Observability: per-channel delivery rates, provider errors, and end-to-end latency.

Back-of-envelope

  • Steady state: assume 30,000 notifications/s average across channels, 60% push, 35% email, 5% SMS. That is 18,000 push/s, 10,500 email/s, 1,500 SMS/s.
  • Burst: a broadcast to 20M users in 5 minutes → 67,000 notifications/s on top of steady state, but provider rate limits (assume the push provider caps us at tens of thousands of sends per second per app, and email providers at a similar order) mean the drain rate, not the enqueue rate, is the bottleneck. The queue must hold the whole burst: 20M × 1 KB ≈ 20 GB. Trivial for a durable log.
  • Storage for status: each notification produces a status record of ~300 B kept for 30 days. 30,000/s × 86,400 × 30 × 300 B ≈ 23 TB. Sharded by notification id with time-based expiry.
  • Device registry: 500M users × 2 devices × 200 B (token, platform, app version, last seen) ≈ 200 GB. Read on every push; cache heavily.
  • Preference lookups: one per (user, category) per notification → 30,000 lookups/s steady, 100,000/s in bursts. Cache with a short TTL; the source is a small table.
  • Template rendering: 30,000 renders/s of a small template is a few dozen cores; not a constraint.
  • Dedup store: keys for the last 24 hours → 30,000 × 86,400 ≈ 2.6B entries × 40 B ≈ 100 GB. A sharded in-memory store with TTL, or a bloom filter in front of a persistent set.

Last reviewed 8 September 2026