System Design · Architecture under constraints
How would you design a notification system?
Short answer
Accept notification requests, apply preferences and deduplication, queue per channel, and deliver through providers with retries and tracking. Separate acceptance from delivery and make each step idempotent.
Assume email, SMS, push and in-app channels, user preferences, templated messages, and delivery tracking. The system must not spam users, must survive a provider outage, and must report whether a notification was sent, delivered, opened or failed.
Pipeline
- Ingest API accepts an event or a direct notification request with an idempotency key
- Preference and consent service decides which channels are allowed
- Deduplication and rate limiting prevent repeated or excessive sends
- Template rendering produces channel-specific content
- Per-channel queues isolate slow or failing providers
- Provider adapters send and record status callbacks
Separate the accept path from the deliver path. The API should return quickly after durably recording the request, not after calling a provider. A queue buffers spikes and lets each channel retry independently. If SMS is down, email and push should still work.
Idempotency applies at two levels: the request and the send. A retried request should not create a second notification, and a retried provider call should not send the same message twice when the provider supports a deduplication id. Track a notification id through every attempt so callbacks can be matched to the original request.
Retries need backoff, jitter and a cap. Classify errors: a transient 5xx or timeout is retryable, while an invalid destination or a hard bounce is permanent and should go to a suppression list. After the retry limit, move the message to a dead-letter queue with enough context to replay or investigate.
Follow-ups you should expect
Interviewers use these to check whether the first answer was understanding or memorisation.
- 1How do you prevent duplicate notifications after a retry?
- 2How do you isolate a failing SMS provider?
- 3What would you do with a hard bounce versus a timeout?
- 4How do quiet hours interact with queueing?
Say it in the interview
I would accept a notification request, check preferences and deduplication, render it, and enqueue it per channel rather than calling a provider inline. Each channel has its own queue, retry policy and dead-letter path so one failing provider does not block the others. I use idempotency keys at the request and send level, and track the notification id through provider callbacks. Consent and quiet hours are enforced before enqueueing, with an audit trail.
Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.
Related questions
Reading is step one
Now say it out loud.
Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live System Design mock interview and get scored feedback on depth, clarity and confidence.