System Design · Design Question
Design a Notification System
Multi-channel notifications: push, SMS, email — templates, preferences, fan-out, retries, and provider abstraction.
Concrete example: Order shipped → API accepts the event, queues it, and a worker sends push "Order #123 on the way" plus an email with tracking — unless the user disabled email in preferences.
1. Requirements
Functional
- Send notifications on events (order shipped, new follower, OTP).
- Channels: mobile push (FCM/APNs), email, SMS; in-app inbox optional.
- User preferences and quiet hours; unsubscribe / opt-out.
- At-least-once delivery to providers; idempotent per notification id.
- High throughput bursts (campaigns) without killing transactional OTPs.
Non-functional
- Absorb campaign bursts without delaying OTPs or security alerts.
- Retry safely, isolate provider failures, and keep an auditable status trail.
Back-of-envelope
2. API
3. Data model
4. High-level design
5. Deep dives
Priority lanes
OTP and security alerts get a dedicated high-priority queue and provider account so a marketing blast cannot starve them.
Idempotency
Fan-out
One event → N devices. Expand device tokens in the push worker; batch provider APIs where supported. Soft-fail invalid tokens and remove them.
Rate limits
Respect provider quotas; token-bucket per channel. Backpressure via consumer lag metrics.
6. Scaling
- Partition Kafka by
userIdfor per-user ordering if needed. - Autoscale workers on lag; separate pools per channel.
- Template + prefs cached in Redis.
- Multi-region: send from region near user or near provider endpoints.
7. Observability
- Acceptance rate, provider latency, bounce/complaint rates.
- DLQ depth alerts; replay tooling.
8. Edge cases
- User disables SMS → skip channel, still try push if allowed.
- Partial success: email sent, push failed → retry push only.
- PII in templates → redact logs; encrypt at rest.
9. Template rendering
Keep templates versioned. Render with strict escaping to avoid injection
into HTML email. Support locale fallback: en-IN → en.
Preview API for product teams reduces production mistakes.
10. Compliance
- SMS/email marketing needs opt-in evidence; transactional may differ by region.
- Honor unsubscribe links quickly; store preference changes durably.
- Log delivery without logging OTP codes in plaintext.
11. Failure modes
- Provider outage → retry with backoff, circuit-break, and route to a backup provider where allowed.
- Poison template/data → stop after bounded retries and move only that item to DLQ.
- Preference service is unavailable → use a safe cached decision; never send marketing without consent.
Interview talking points
- Lead with: Protect transactional traffic with a separate priority lane.
- Accept API → durable queue → channel workers → providers.
- Separate transactional vs bulk lanes.
- Prefs, quiet hours, templates before send.
- Idempotency keys prevent duplicate OTPs/blasts.
- Track per-channel attempts; DLQ poison messages.
- Respect provider rate limits; clean invalid push tokens.
- Priority: never let marketing block OTP.