Hey folks, Rahul here ๐
Every app has notifications. Most implement them badly โ a polling loop that hammers the server, an unread count that's always wrong, and grouping logic that shows "John and 47 others liked your post" as 48 separate items. Sound familiar?
Let's design a notification system that handles real-time delivery, intelligent grouping, cross-tab synchronization, and the deceptively complex read/unread state machine.
R โ Requirements
Functional Requirements
Display in-app notification bell with unread count badge
Notification dropdown/panel with grouped notifications
Mark individual or all notifications as read
Real-time delivery โ new notifications appear without refresh
Push notifications (browser) with user opt-in
Notification preferences (per-category enable/disable)
Click-to-navigate: each notification links to relevant content
Non-Functional Requirements
Real-time: <2s delivery from event to UI
Consistency: Unread count must be accurate across tabs
Performance: Support users with 10K+ notifications
Battery: Mobile-friendly โ no aggressive polling
Graceful degradation: Works without WebSocket (falls back to polling)
A โ Architecture
Delivery Strategies
StrategyLatencyBatteryComplexityUse WhenPollingHigh (interval)PoorLowFallback onlyLong PollingMediumOKMediumSSE unavailableSSE (Server-Sent Events)LowGoodMediumRead-only stream โ WebSocketLowestGoodHighAlready have WS for other features
Recommendation: SSE for notifications specifically. It's simpler than WebSocket, works over HTTP/2, auto-reconnects, and notifications are inherently server โ client (no bidirectional need).
Component Architecture
Notification Flow
D โ Data Model
Notification Types and Grouping
Client State
Grouping Logic
I โ Interface Definition
SSE Connection
Mark as Read โ Optimistic with Batching
O โ Optimizations
1. Cross-Tab Synchronization
2. Unread Count Badge Animation
3. Browser Push Notifications
4. Notification Preferences
Production Gotchas Rahul Has Debugged ๐ฅ
Unread Count Drift: If a notification is created between the initial fetch and SSE connection, you miss it. Solution: SSE connection sends a
count_syncevent every 60s to reconcile.Self-Notifications: Don't notify users about their own actions. The backend must filter
actor_id !== recipient_idโ never rely on the frontend for this.Notification Storms: A viral post generates thousands of likes. Without grouping, the user gets 1,000 notifications. Backend should coalesce within a time window (e.g., group all likes within 5 minutes into one notification).
SSE Memory Leak:
EventSourceaccumulates all messages in memory if you don't handle them. Always consume events promptly. Some browsers also leak if you create/destroy EventSource instances rapidly.Mobile Tab Backgrounding: iOS Safari kills SSE connections when the tab is backgrounded. On
visibilitychangeโ visible, reconnect and fetch missed notifications sincelastEventId.
Next up: #11: Design a Drag-and-Drop Kanban Board โ reorder algorithms, optimistic multi-list moves, and the fractional indexing trick. ๐