AI Fires Notifications with No Strategy
AI generates notifications with: synchronous sending in the request handler (user waits while the email sends), one channel only (email for everything, no push or in-app), no user preferences (users cannot choose which notifications they receive or how), no batching (10 comment replies = 10 separate emails in 5 minutes), no templating (notification content hardcoded in business logic), and no delivery tracking (did the email arrive? Was the push received? Unknown). Users are spammed, then they unsubscribe from everything.
Modern notification systems are: async (queue-based delivery, user gets immediate response), multi-channel (email, push, in-app, SMS — user chooses per notification type), preference-managed (granular opt-in/opt-out per channel per notification category), batched (digest: 10 comment replies become one "10 new replies" email), templated (notification content in templates, not business logic), and tracked (delivery status, open rates, failure retries). AI generates none of these.
These rules cover: multi-channel delivery architecture, user preference centers, intelligent batching and digest, template-based notification content, delivery tracking with retry, and notification fatigue prevention.
Rule 1: Multi-Channel Delivery Architecture
The rule: 'Design notifications as channel-agnostic events that are routed to channels based on user preferences. Event: { type: "comment.reply", userId: "123", data: { commentId, articleTitle, replyAuthor } }. The notification router checks user preferences and delivers to: email (if subscribed), push (if device registered), in-app (always — stored in the notifications table), and SMS (for critical alerts only). One event, multiple channels, user-controlled routing.'
For the architecture: 'Business logic publishes notification events to a queue. The notification service consumes events and: (1) resolves user preferences for this notification type, (2) for each enabled channel, renders the template with event data, (3) dispatches to channel-specific senders (SendGrid for email, FCM for push, database INSERT for in-app), (4) records delivery status per channel. The business logic knows nothing about channels, templates, or delivery — it publishes events.'
AI generates: await sendEmail(user.email, 'You have a new reply', replyText) inline in the comment handler. Adding push notifications means modifying the comment handler. Adding SMS means modifying it again. With event-based delivery: the comment handler publishes comment.reply. Adding a new channel: add a sender to the notification service. Zero changes to business logic.
- Channel-agnostic events: { type, userId, data } — business logic publishes, notification service routes
- Multi-channel: email, push, in-app, SMS — routed by user preferences per notification type
- Queue-based: async delivery, user gets immediate response, retries on failure
- Channel senders: SendGrid (email), FCM (push), database (in-app), Twilio (SMS)
- Adding a channel: new sender in notification service, zero changes to business logic
Business logic publishes comment.reply. The notification service routes to email, push, and in-app based on user preferences. Adding SMS: add a sender in the notification service. Zero changes to the comment handler. Channel-agnostic events decouple business logic from delivery.
Rule 2: User Preference Centers
The rule: 'Every user has a notification preferences matrix: notification type (rows) x channel (columns). Example: comment replies: email ON, push ON, in-app ON. Marketing updates: email OFF, push OFF, in-app ON. Security alerts: email ON, push ON, in-app ON, SMS ON. Users control every cell in this matrix from a preference center UI. Default preferences are opt-in for essential notifications and opt-out for marketing.'
For the preference schema: 'Table notification_preferences: userId, notificationType (enum), channel (enum), enabled (boolean). Default: if no preference record exists, use the system default for that notification type. Override: user explicitly sets enabled = true or false. The preference check is: SELECT enabled FROM notification_preferences WHERE userId = X AND notificationType = Y AND channel = Z. If no row: use the default from the notification type definition.'
AI generates: no preferences. Users receive every notification on every channel. A user who comments frequently gets 50 emails a day. They click unsubscribe on one email and are unsubscribed from everything — including security alerts. Granular preferences: the user disables email for comment replies but keeps push notifications and security alerts. They control their experience without losing critical notifications.
Rule 3: Intelligent Batching and Digest
The rule: 'Batch related notifications into digest messages instead of sending each individually. Pattern: when a notification event arrives, check if there are pending notifications of the same type for the same user. If yes, increment the batch counter and delay sending. After a batch window (5-15 minutes) or a maximum batch size (10 notifications), send one digest: "You have 10 new replies on your article." The user gets one email instead of 10.'
For digest strategy: 'Time-based: collect notifications for 15 minutes, then send one digest. Count-based: send when 5 notifications accumulate, regardless of time. Hybrid: send after 15 minutes OR 10 notifications, whichever comes first. Urgent notifications (security alerts, direct messages) bypass batching and send immediately. The batch window is configurable per notification type: comment replies (15 min batch), likes (1 hour batch), security alerts (no batch).'
AI generates: 10 comment replies = 10 emails in 5 minutes. The user sees 10 unread emails, opens each one, reads a one-line reply, deletes. With digest: one email listing all 10 replies, sent 15 minutes after the first. The user reads all replies in context. Less inbox clutter, less notification fatigue, and the user is more likely to engage (one actionable email vs 10 trivial ones).
- Batch related notifications: 10 replies = 1 digest email, not 10 separate emails
- Time-based (15 min), count-based (5 notifications), or hybrid batch windows
- Urgent notifications bypass batching: security alerts, direct messages send immediately
- Configurable per notification type: comments (15 min), likes (1 hour), alerts (instant)
- Digest format: summary count + list of individual items + single action link
10 comment replies = 10 emails in 5 minutes. User opens each, reads one line, deletes. Digest: one email with all 10 replies in context, sent 15 minutes after the first. Less inbox clutter, higher engagement, lower unsubscribe rate.
Rule 4: Template-Based Notification Content
The rule: 'Notification content lives in templates, not in business logic. Each notification type has templates per channel: comment_reply.email.html, comment_reply.push.json, comment_reply.inapp.json. Templates receive event data and user data: {{ replyAuthor }} replied to your comment on "{{ articleTitle }}": "{{ replyExcerpt }}". Template engines: Handlebars, Liquid, or React Email for HTML emails. Changing notification wording: edit a template, no code deployment needed.'
For channel-specific formatting: 'Email: full HTML with images, links, and rich formatting (React Email or MJML for responsive email templates). Push: title (50 chars) + body (100 chars) + action URL + optional image. In-app: structured JSON { title, body, actionUrl, icon, read: false }. SMS: plain text, 160 chars, essential info only. Each channel has different constraints — the template system outputs the right format for each.'
AI generates: sendEmail(user.email, 'New reply', `${author} replied: ${text}`) — notification content hardcoded in the comment handler. Changing the wording requires a code change and deployment. Adding an unsubscribe link requires modifying every email send call. Template system: edit the template, deploy nothing. Add an unsubscribe link to the email layout template — all emails get it automatically.
Rule 5: Delivery Tracking and Retry
The rule: 'Track delivery status for every notification: queued, sent, delivered, opened, clicked, failed, bounced. Store in a notifications_log table: { id, userId, type, channel, status, sentAt, deliveredAt, openedAt, failedReason }. Retry failed deliveries: exponential backoff (1min, 5min, 30min), max 3 retries. After max retries: mark as permanently failed, alert the operations team if failure rate exceeds threshold.'
For email-specific tracking: 'Webhook integration with the email provider (SendGrid, Postmark, SES) for delivery events: delivered, bounced, complained (spam report), opened, clicked. On bounce: mark the email as invalid, stop sending to it (prevent reputation damage). On complaint: auto-unsubscribe the user from that notification type. On persistent failure: switch to a different channel (email fails, try push). Email reputation is fragile — one bounce is a data point, 100 bounces blacklist your domain.'
AI generates: sendEmail() with no error handling, no delivery tracking, and no retry. The email API returns an error: the notification is lost. No record that it was attempted, no retry, no fallback. With tracking: the failure is recorded, retried 3 times with backoff, and if all retries fail, the user receives an in-app notification instead. No notification is silently lost.
Sending to invalid email addresses damages your domain reputation. On bounce: mark email invalid, stop sending. On spam complaint: auto-unsubscribe the user. Email reputation is fragile — webhook integration with your provider catches these events before they compound.
Complete Notification Systems Rules Template
Consolidated rules for notification systems.
- Channel-agnostic events: business logic publishes, notification service routes to channels
- User preference matrix: notification type x channel, granular opt-in/opt-out
- Intelligent batching: digest 10 replies into 1 email, urgent notifications bypass
- Template-based content: Handlebars/React Email, channel-specific formatting
- Delivery tracking: queued > sent > delivered > opened > clicked > failed per notification
- Retry with backoff: 3 attempts, exponential delay, fallback to alternate channel
- Email reputation: auto-unsubscribe on complaint, stop sending on bounce
- Notification fatigue prevention: batching + preferences + frequency caps per user