System Design · Lesson 15 of 15
Notification System
Push, email, SMS and in-app notifications with fan-out, templates and preferences.
- Advanced
- 40 min read
- 3 objectives
Before this lessonLesson 14: Typeahead and Nearby Search
What you will learn
- Fan-out events to channels
- Apply preferences, quiet hours and collapse
- Avoid celebrity-scale push storms
Your Progress
0 of 15 lessons 0%
- Lessons0 / 15
- Completed0
- Est. time left~ 8 hours
Create a free account to keep your progress on every device.
Design a notification system that can send push, email, SMS and in-app events — password resets, likes, delivery updates — without waking every follower of a celebrity, and without spamming someone at 3 a.m. for a like.
Step 1 — Clarify
- Channels: iOS/Android push, email, SMS, in-app inbox?
- Who produces events — many product services?
- User preferences, quiet hours, marketing vs security?
- Do we collapse bursts ('Alice and 79 others liked your photo')?
v1: all four channels, templates, preferences, quiet hours for marketing, collapse window, per-user rate limits. Security events (2FA, password reset) ignore marketing opt-out and quiet hours.
Step 2 — Pipeline
Producers enqueue a high-level event. Workers expand it, apply preferences, and talk to each provider. Channels scale independently.
Step 3 — API and data
POST /v1/events
{ "event_type": "comment_on_post",
"actor_id": 9,
"object_id": 441,
"recipient_ids": [3, 4] } -- or an audience: "followers_of"
templates(event_type, channel, locale, body)
devices(user_id, token, platform, last_seen)
preferences(user_id, event_type, channel, enabled)
inbox(user_id, notif_id, body, read, ts) -- partition by user_idCallers pass intent, not a rendered string. The notification service owns templates so copy can change without redeploying every product. Idempotency key: (event_type, object_id, recipient_id, time_bucket) so retries do not double-send.
Step 4 — Fan-out
- Small N (ride update, 2FA): look up devices, send immediately.
- Large N (celebrity post): do not create 40 million pushes. Write the post once. In-app feed uses the hybrid fan-out from the news-feed lesson. Push only recently active users who opted in, or skip push and badge the app. Everyone else sees it next open.
- Kafka / log partitions by
user_idso one person's notifications stay ordered.
Step 5 — Preferences, quiet hours, collapse
- Preferences cached in Redis. Security events ignore marketing opt-out.
- Quiet hours: delay marketing into a per-user local-time window; never delay 2FA.
- Collapse: 80 likes in 45 seconds → one 'Alice and 79 others liked your photo'. Buffer by
(recipient, event_type, object)before send. - Per-user rate limit: cap pushes per hour; overflow goes in-app only so a chatty group does not drain the battery.
Step 6 — Providers and reliability
APNs and FCM are queues of their own. Store device tokens, handle invalid-token callbacks, backoff on 5xx. Email/SMS providers have their own rate limits — a token bucket per provider. The in-app inbox is a partition per user, same shape as a chat inbox. Metrics: enqueue rate, send success, provider latency, unsubscribes. Dead-letter poison templates. Multi-region: inbox shards near the user; providers are global.
// Write your solution here
