Wrap-Up
Interview Tips
- Frame it as fan-out + reliable delivery, not “send an email.” The interesting engineering is preferences, dedup, burst absorption, and provider failure handling — steer there quickly.
- Lead with at-least-once + idempotent dedup = effectively-once. Stating this delivery-semantics contract up front shows you understand the core correctness problem. Interviewers probe “what if it’s delivered twice?” — have the
SETNX answer ready. - Separate transactional from promotional early. This single distinction drives priority queues, quiet-hours policy, and rate-cap behavior. It’s the highest-signal design decision.
- Kafka as a shock absorber is the key insight for fan-out storms — the backlog drains at a provider-safe rate. Say why you don’t push 10M sends synchronously.
- Bulkheads + circuit breaker per provider is the resiliency talking point. Explain transient-vs-permanent failure classification (retry vs DLQ).
- Likely follow-ups: “how do you not spam users?” (preferences + rate caps), “how do you know it was delivered?” (provider webhooks + status log), “how do you handle a provider outage?” (circuit breaker + retry queue + eventual drain), “how do you localize?” (template + locale).
- Don’t over-index on the database — the queues and policy engine are where points are won.
Resiliency
- Durable-before-ack: an accepted notification is persisted/enqueued before the 202, so a crash never silently drops it.
- Kafka retention & replay: downstream outages become backlogs, not data loss; the DLQ enables replay after a fix.
- Bulkheads: per-channel queues and worker pools isolate a slow/broken provider from healthy channels. See /dev-docs/design-concepts/reliability/bulkhead/.
- Circuit breakers + backoff: stop hammering a failing provider; exponential backoff with jitter prevents synchronized retry storms. See /dev-docs/design-concepts/reliability/circuit-breaker/.
- Backpressure: token buckets shape outbound rate; excess waits in Kafka rather than overrunning providers. See /dev-docs/design-concepts/reliability/back-pressure/.
- Idempotency everywhere: dedup keys make retries and duplicate events safe.
- Multi-region: stateless ingest/processor tiers run active-active; Kafka and stores replicate across regions.
Observability
- SLIs: ingest acceptance latency & error rate; end-to-end delivery latency per priority (transactional p99 < 5 s); per-channel delivery success rate; provider error rate; queue depth / consumer lag per channel; DLQ rate; dedup hit rate.
- Golden alerts: transactional delivery p99 breaching SLO, consumer lag growing (drain slower than intake), provider circuit breaker open, DLQ rate spike, dedup store unavailable.
- Tracing: propagate the
notification_id and upstream trace id from ingest → processor → worker → provider so a single notification’s journey is reconstructable. - Dashboards: notifications/sec by category & channel, delivery funnel (accepted → sent → delivered → opened), provider health, backlog burn-down during fan-out storms.
- Auditing: immutable notification log for support (“why didn’t I get notified?”) and compliance (opt-out honored).
Concepts Used
- /dev-docs/design-concepts/messaging/kafka/ — buffering backbone, per-user partitioning, replay
- /dev-docs/design-concepts/specialized/notification-fanout/ — fan-out strategies
- /dev-docs/design-concepts/messaging/dlq-and-retry/ — dead-lettering and retry policy
- /dev-docs/design-concepts/distributed/idempotency/ — dedup / effectively-once
- /dev-docs/design-concepts/rate-limiting/algorithms/ — per-provider token-bucket shaping
- /dev-docs/design-concepts/reliability/circuit-breaker/ & /dev-docs/design-concepts/reliability/bulkhead/ — provider failure isolation
- /dev-docs/design-concepts/reliability/back-pressure/ — burst absorption
- /dev-docs/design-concepts/storage/wide-column-stores/ — the notification log