Design a Notification System

Intermediate · 24 min read · High-Level Design, Case Studies: Core

Multi-channel delivery, preferences, retries and deduplication.

Design a notification platform that other teams call to reach users through push notifications (iOS/Android), SMS, email and in-app messages — order updates, security alerts, marketing campaigns, reminders.

1. Clarify the problem

2. Requirements

Functional

3. Capacity estimation

So: the dispatch tier must scale horizontally behind queues; status storage is a write-heavy time-series workload (wide-column store with TTL); provider rate limits — not our servers — are often the real bottleneck.

4. API

Return 202 Accepted — sending is asynchronous.

5. Data model

Status, delivery and inbox tables are high-volume and accessed by key and time → Cassandra/DynamoDB with TTLs. Preferences and templates are small → relational DB plus cache.

6. High-level design

Flow

7. Deep dives

A 100M-user campaign enqueues 100M jobs. If an OTP lands behind them in the same queue, it arrives an hour late.

8. Failure modes

9. Trade-offs summary

10. What interviewers look for

Interview questions

  • How do you make sure an OTP is delivered quickly while a 100-million-user marketing campaign is running?
  • A worker crashes after the SMS provider accepted the message but before recording success. What happens?
  • How would you implement 'no more than 3 marketing pushes per user per day'?

Practice

Write your answer before opening the suggested one. Anything you mark "Partly" or "Missed it" comes back in your review deck.

Further reading

Key takeaways

  • Accept requests asynchronously, then route through per-priority and per-channel queues.
  • Isolate critical notifications from bulk campaigns in queues, workers and provider capacity.
  • Deduplicate at the API, worker and provider boundaries; at-least-once is the realistic guarantee.
  • Respect preferences, consent, quiet hours and frequency caps in a central router.
  • Use multiple providers with circuit breakers, classify errors, and clean up stale device tokens.

Previous: Design a Distributed Rate Limiter · Next: Design Search Autocomplete