Designing Real SystemsLesson 5.3
How to design a notification system
push vs pull, notification fanout, message queues, multi-channel delivery, retry logic, notification templates, user preference management
Notification System Architecture
A notification system must deliver messages across multiple channels (push, email, SMS) reliably without blocking the main application flow.
Core Architecture
- Application generates an event (order placed, mention, follow)
- Event is published to a message queue (Kafka, SQS)
- Notification service consumes events and determines which users to notify
- Worker pools handle delivery per channel
Push vs Pull
- Push: server sends notification to device (FCM for Android, APNs for iOS). Low latency. Device must be registered.
- Pull: client polls for new notifications. Simpler but higher latency and server load.
Fanout at Scale
# Fanout approach for a post with 10M followers
# Option 1: Fanout on write (precompute) - fast reads, slow writes
def on_post_created(post):
followers = db.get_followers(post.user_id) # 10M users
for follower_id in followers:
queue.push(create_notification(follower_id, post))
# Option 2: Fanout on read - slow reads, fast writes
def get_notifications(user_id):
following = db.get_following(user_id)
return db.get_recent_posts(following, limit=20)Retry and Deduplication
Delivery failures are common (expired device tokens, temporary service outages). Use exponential backoff and idempotency keys to prevent duplicate notifications on retries.
