A mistake I see in many microservice systems:
Every service sends emails.
Auth sends emails. Billing sends emails. User service sends emails.
Soon you have 5 different email implementations, 5 retry strategies, 5 ways to get rate-limited.
The fix: one notification service. Every service emits events — the notification service owns delivery, retries, rate limits, and user preferences.
Notifications are deceptively complex. Once you add retries, rate limits, preferences, and multiple channels, it deserves its own service.
Top comments (3)
The "5 retry strategies, 5 ways to get rate-limited" framing is the real cost. One notification service also gives you a single place for user preferences and quiet hours, which is nearly impossible when every service rolls its own.
Five different retry strategies is the part that stands out, email looks simple until every team reimplements backoff and bounce handling slightly differently. Emitting events and letting one service own delivery also makes rate limits much easier to reason about.
Exactly — and the worst part is retries that aren't safe to repeat. Every team reinvents backoff slightly differently, and half of them retry against a non-idempotent send, so a timeout turns into a duplicate email. The two things I'd standardize: retries must be idempotent first (same payload key, safe to replay N times), and backoff needs jitter, not just exponential growth — five services with the same fixed backoff will thundering-herd the same rate limit together. Once those hold, rate limits really do become tractable: you know the effective ceiling per provider and can pace the whole queue against it.