Design a notification system: push, SMS and email
Notifications look simple until you send millions across push, SMS and email. We design a reliable, respectful pipeline.

Goal: other services call one notification system to send messages to users by push, SMS, email or in-app, reliably and without spamming.
Requirements
- Multiple channels and templates (many languages).
- Respect user preferences and quiet hours.
- Reliable delivery with retries, no duplicates.
- Handle spikes (a sale starts and millions are notified).
- Track delivery status.
Architecture
- API / event intake: services send "notify user X about event Y" with a template ID and data.
- Notification service: validates, looks up the user's preferences and contact info, checks rate limits, renders the template, and decides channels.
- Queues: one queue per channel (push, SMS, email) so a slow SMS provider doesn't block push.
- Workers per channel: take messages and call the provider (APNs and FCM for push, an SMS gateway, an email service).
- Status tracking: record sent, delivered, failed, opened.
Why queues
They absorb spikes, isolate failures between channels and allow retries with exponential backoff. After repeated failures, a message goes to a dead-letter queue for inspection.
Avoiding duplicates
Providers and queues are at-least-once. Give each notification a unique dedup key (for example user + event + template) and skip a repeat within a time window. Make workers idempotent.
Priorities
Separate high-priority traffic (one-time passwords, security alerts) from bulk marketing, with its own queue and capacity, so a campaign never delays a login code.
Preferences and compliance
- Let users opt out per channel and category. Transactional messages and marketing have different rules.
- Honour unsubscribe links and local regulations.
- Keep opt-in/out records.
Rate limiting and batching
Limit how many notifications one user gets per hour, and batch or digest low-priority ones ("5 new comments"). Respect provider rate limits too.
Failures to plan for
Invalid push tokens (remove them), bounced emails (stop sending), a provider outage (fail over to a second provider), and templates with missing data (validate before sending).
Observability
Track queue depth, latency from event to delivery, provider error rates and delivery success per channel.
Takeaway: decouple with per-channel queues, make sends idempotent, separate urgent from bulk, honour preferences, and plan provider failover.
System Design series, Part 3 (case studies): 8 of 16. Previous: Design a payment system for a mobile wallet.


