Skip to content

Preferences, Frequency & State

Preferences, frequency and state is the practice of modelling notification choices as durable user data that the delivery system consults before sending. It covers channels, topics, opt-outs, quiet hours and digest cadence, so events can be delivered, deferred, grouped, expired or suppressed without losing the underlying message.

Notification bugs often come from treating preferences as a screen-level setting or a conditional buried in each caller. One code path checks email opt-out, another ignores quiet hours, and a retry job bypasses both. The result is either noisy users or missing communication. The system needs a single, durable source of truth for what the user has allowed and how delivery should behave.

A robust design separates creating a notification from delivering it. When an application event occurs, write a notification, intent or outbox record that represents the thing worth communicating. A worker then reads the user’s preference state, current time, topic, channel rules and expiry policy. It may send now, schedule for later, add to a digest, mark as intentionally suppressed or expire it explicitly.

The main cost is more state and more lifecycle management. You now need schemas, defaults, validation, audit trails, idempotent workers, retry handling and clear ownership of preference changes. It also introduces policy questions: if a user changes preferences after an event was queued, should old items follow the old rule or the new one? The honest answer depends on product semantics, compliance requirements and user expectations.

Engineers meet this in notification services, chat products, activity feeds, billing alerts, incident tools and marketing systems. The same pattern appears as preference tables, notification queues, outbox records, scheduled jobs and digest builders. Quiet hours and digests should not delete work; they should alter delivery timing. Every important item should end in a visible state: sent, deferred, bundled, expired or deliberately suppressed.

Common questions

Why not just check notification preferences inside the send function?
A send-time check is too late and too narrow. It cannot reliably handle digesting, quiet hours, retries, auditing or multiple channels, especially when different callers invoke sending in different ways. Centralising preference evaluation in the delivery pipeline makes policy consistent and keeps the original notification record available for later action.
Does quiet hours mean the notification should be dropped?
No. Quiet hours are a scheduling rule, not proof that the event is irrelevant. If the message will still matter later, keep it queued or scheduled. If it will not, expire it through an explicit policy. Dropping it silently makes it impossible to distinguish user-respecting delay from accidental data loss.
What happens when a user changes preferences while notifications are already queued?
It depends on the meaning of the notification and the promises made to the user. Some systems apply the latest preference at delivery time; others snapshot policy when the notification is created. Either approach can be valid, but it should be deliberate, documented and testable, not an accidental side effect of where the check happens.
How do digests avoid losing individual messages?
A digest should be built from stored notification records, not from transient events held only in memory. Each eligible item remains addressable until the digest is sent, expired or superseded. After delivery, the system records that the item was included, which helps prevent duplicates and supports debugging when users report missing updates.