Notification Preferences
A notification system that ignores preferences becomes a spam system. Preferences are not just UI settings. They are part of the delivery decision.
Channels and Categories
Channels:
Email
SMS
Push
WhatsApp
In-app
Categories:
Security
Transactional
Product activity
Marketing
Promotional
System maintenance
The preference question is not "email on or off." It is "Can this user receive this category on this channel?"
Preference Matrix
Category Email SMS Push In-app
Security required optional urgent required required
Transactional enabled optional enabled enabled
Product activity optional disabled optional enabled
Marketing optional disabled optional optional
Security alerts may be mandatory. Marketing must be optional. Product activity is often configurable.
Schema
CREATE TABLE notification_preferences (
user_id UUID NOT NULL,
category TEXT NOT NULL,
channel TEXT NOT NULL,
enabled BOOLEAN NOT NULL,
source TEXT NOT NULL DEFAULT 'user',
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (user_id, category, channel)
);
Use source to distinguish user action, admin policy, legal import, or default.
Decision Function
async function canSendNotification(input: {
userId: string;
category: string;
channel: string;
}) {
if (input.category === "security") return true;
const preference = await getPreference(
input.userId,
input.category,
input.channel
);
return preference?.enabled ?? defaultPreference(input.category, input.channel);
}
Defaults should be conservative for marketing and practical for transactional messages.
Fallback Channels
Fallback is not always good. If push fails for marketing, do not automatically send SMS. If push fails for a suspicious login, SMS fallback may be appropriate.
Marketing push failed -> no fallback
Security push failed -> try email + SMS
Invoice email failed -> retry email, then support queue
Preference UI
The UI should map to categories users understand:
Security alerts
Order and payment updates
Product activity
Tips and marketing
Do not expose internal event names like PaymentSuccessV2.
Preference design is product design. Clear language reduces support tickets and legal risk.
Caching Preferences
Preference reads happen often. Cache carefully.
Cache key: notification_preferences:user_123
TTL: 5 minutes
Invalidate when user updates preferences
Never cache forever. A user unsubscribe should take effect quickly.
Common Mistakes
- One global unsubscribe switch for every message type.
- Sending fallback SMS for marketing.
- Ignoring preference changes due to stale cache.
- Exposing internal event names to users.
- Not auditing preference changes.
Interview Questions
- Which notification categories can be mandatory?
- How would you model per-channel preferences?
- When should fallback channels be used?
- How would you cache preferences safely?
Exercise
Create a preference matrix for a GitHub-like collaboration app with mentions, pull request reviews, security alerts, and newsletters.
What you will learn
How categories and channels combine into preferences.
Which notifications can be mandatory.
How fallback channels should work.
How to model preferences for fast lookup.
Production checklist
- Preference categories are defined
- Mandatory alerts are documented
- Channel opt-outs are respected
- Fallback rules are explicit
- Preference lookup is cached safely
- Audit trail exists