Skip to content

title: Preference schema decoupling type: concept created: 2026-04-24 updated: 2026-04-24 tags: [preference-system, schema-design, decoupling, orthogonality, notifications, user-settings, conflated-enum] sources: [2026-03-19-slack-how-slack-rebuilt-notifications] related: patterns/decouple-what-from-how-in-preferences, concepts/separation-of-concerns, concepts/explicit-state-over-implicit-sync, concepts/mental-model-preference-coherence, systems/slack-notifications-2-0


Preference schema decoupling

Preference schema decoupling is the structural discipline of representing a user preference as a tuple of orthogonal axes rather than a single conflated enum, when the preference genuinely has independent dimensions the user cares about. The anti-pattern is the one-enum-for-many- things shape, where the preference encoding forces users to trade off axes they'd prefer to control independently.

Canonical instance: Slack notifications

Slack's legacy notification preference conflated notification content (what gets a notification) with delivery channel (push on/off) into a single enum:

'desktop': everything | mentions | nothing   // Push on desktop

Wanting "fewer push notifications" forced users to set nothing, which also disabled in-app badges — they lost awareness as the cost of losing interruption. Slack's framing (canonical quote): "What users were notified about was tightly coupled with how they received notifications. Wanting fewer push notifications meant sacrificing in-app awareness entirely — there was no way to separate the two."

The decoupled schema splits this into two axes:

'desktop':              everything | mentions  // What: in-app content
'desktop_push_enabled': true | false            // How: delivery channel

Users now compose the cross-product: full awareness with no push, push for mentions only, no awareness and no push, etc. The framing: "In-app notifications and activity are consistent across all clients, but push notifications are further customizable on desktop and mobile."

Structural test: is it decoupling or just schema width?

Not every preference benefits from decoupling. The diagnostic is does the user care about the axes independently?

  • If users frequently want to change one axis without the other → decouple.
  • If the axes are genuinely dependent (e.g. "notification sound" is only meaningful when "notification delivery" is on) → keep them conditional, but the underlying schema can still decouple and the UI can gate.
  • If the combinations users don't want are the ones the decoupling creates (no combination is illegal) → decouple.

Slack's legacy everything / mentions / nothing fails all three tests — users clearly wanted "mentions for in- app + no push" (a combination the legacy schema couldn't express), which is the smoking-gun signal that the schema was wrong.

Why the legacy schema accumulates

Preference schemas like Slack's "accumulate" over years — the legacy conflated enum is usually the right shape for v1 (small team, simple product, single delivery channel) and becomes wrong as the product grows. Typical accumulation paths:

  • v1: one channel, one enum. Clean.
  • v2: second channel added as its own enum. Acceptable.
  • v3: per-platform push toggles added as hidden advanced controls. Discoverability breaks.
  • v4: sync logic added between platforms. Now the schema has implicit derivation rules.
  • v5: power-user features grafted on. Mental model collapses.

Slack's four-paradigm pre-rebuild state is a textbook late-stage version of this accumulation. The rebuild returned the schema to orthogonal-from-the-start shape.

Relationship to separation-of-concerns

concepts/separation-of-concerns at the preference-schema altitude. Each concern (what to notify about, how to deliver, advanced-per-platform overrides) gets its own dimension. The implementation layer (read-time translation, cross-platform sync) is a separate concern from the schema shape.

Contrasts

vs concepts/explicit-state-over-implicit-sync — these are complementary but distinct. Decoupling splits one enum into two orthogonal values; explicit-state-over- implicit-sync says each platform should have its own stored value rather than one stored value + a derivation rule. Slack's rebuild did both.

vs feature flags — a feature flag is a binary code-path selector and doesn't produce a user-facing preference schema. Decoupled preference axes are user- observable dimensions; flags are internal.

vs configuration API design — decoupled preference schemas are specifically about user-expressible intent, not operator-expressible config. A preference schema lives or dies by its alignment with user mental models (concepts/mental-model-preference-coherence); a config API lives or dies by expressiveness and automation ergonomics.

Seen in

  • systems/slack-notifications-2-0 — canonical instance. Before: everything | mentions | nothing conflating content and channel. After: everything | mentions + desktop_push_enabled: true | false orthogonal.
Last updated · 470 distilled / 1,213 read