Lesson 11 of 15Notification Preferences

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:

EmailSMSPushWhatsAppIn-app

Categories:

SecurityTransactionalProduct activityMarketingPromotionalSystem maintenance

The preference question is not "email on or off." It is "Can this user receive this category on this channel?"

Preference Matrix

CategoryEmailSMSPushIn-app
SecurityRequiredOptional urgentRequiredRequired
TransactionalEnabledOptionalEnabledEnabled
Product activityOptionalDisabledOptionalEnabled
MarketingOptionalDisabledOptionalOptional

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.

SituationFallback
Marketing push failedNo fallback
Security push failedTry email + SMS
Invoice email failedRetry email, then support queue

Preference UI

The UI should map to categories users understand:

Security alertsOrder and payment updatesProduct activityTips 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
Invalidation
Invalidate when user updates preferences

Never cache forever. A user unsubscribe should take effect quickly.

Common Mistakes

  1. One global unsubscribe switch for every message type.
  2. Sending fallback SMS for marketing.
  3. Ignoring preference changes due to stale cache.
  4. Exposing internal event names to users.
  5. Not auditing preference changes.

Interview Questions

  1. Which notification categories can be mandatory?
  2. How would you model per-channel preferences?
  3. When should fallback channels be used?
  4. 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