Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

09 — User Behaviour (Feature Flags Module)

Expected behaviour derived from server semantics + shared interaction rules; common mistakes and UX countermeasures. Purpose: give the UI designer a playbook of what the user does so the flow never surprises.


1. Expected behaviour (server-contract-driven)

  • A toggle is a truth claim: the switch shows server-confirmed state only after reconcile; optimistic apply rolls back on any error (06 §1).
  • Everything the admin changes applies on next read. Today: immediate (no cache). Once the Redis flag cache lands (sl:global:featureflag:{name}, poll + cache 30 s — CACHE_ARCHITECTURE.md:33,48): up to 30 s skew. UI must not promise instant propagation; the client gating cubit re-reads on its own TTL and on pull-to-refresh.
  • Disabled is default for missing keys: isEnabled() returns flag?.enabled ?? false (feature-flags.service.ts:24) — the UI must treat "key absent from enabled set" as OFF everywhere, never as "unknown/loading".
  • Lists are complete, not paginated: all flag list endpoints return full arrays (feature-flags.service.ts:10-20) — no infinite scroll; a tenant catalog is small.
  • Delete removes from every surface but keeps the key slot: soft delete (base.repository.ts:68-74) + unique {tenantId,key} index (feature-flag.schema.ts:26) → re-create can 500 (OQ-4). Users who delete-and-recreate get a confusing failure — countermeasure: warn in delete dialog + error copy.
  • Org overlay replaces wholesale: PATCH /organizations/:id/feature-flags full-replaces the map (organizations.service.ts:149-151) — a UI that edits one key and PATCHes the loaded map without merging silently deletes siblings. Merge-first is mandatory.

2. Common mistakes & countermeasures

MistakeRelatedUX countermeasure
Admin flips a flag and expects it visible on a colleague's phone "right now"propagationinfo banner on toggle success (immediate today; "~30 s" once cache lands); pull-to-refresh affordance
Editing description/module and believing it savedPUT /feature-flags drops them (feature-flag.repository.ts:40)warning banner in editor (06 §3); never render those fields as saved
Bulk-updating then wondering which rows appliedsequential no-transaction bulk (feature-flags.service.ts:40-44)per-row results + "retry failed"
Delete → re-create same key → 500unique index + soft delete (OQ-4)delete dialog warning; error copy with cause hint
Org-level edit wiping sibling flagsfull-replace $set (organizations.service.ts:149-151)merge-before-PATCH; diff preview in the org settings UI (proposed)
Admin assumes permissions protect flagsnone enforced (rbac.guard.ts:29)UI gates affordances by permission anyway; server enforcement (planned) (OQ-5)
Typing keys with spaces/uppercaseno server pattern validationclient hint a-z 0-9 . _ - (proposed); trim on submit
Re-creating a key expecting a clean slateupsert matches on {tenantId,key} — same doc re-enablededitor copy: "Saving this key re-enables the existing flag"

3. Power & new users

  • Power (configurator): bulk selection via long-press on phone / Ctrl+Click on desktop; keyboard N new flag; expects per-row bulk results; copies keys with one tap.
  • New (first-time admin): empty-catalog onboarding: "No flags yet — create one, or contact your platform team" + link to what flags do (help text); first toggle → success snackbar teaches the effect (banner with propagation note).

4. Mobile & a11y users

  • One-thumb: switch rows are the primary action — ≥ 48 dp targets, switch on the right edge; delete in menu, not swipe (destructive, 08 §1: no destructive swipes).
  • TalkBack/VoiceOver: rows announce key + state; toggle via double-tap; bulk results announced (live region); editor errors focus-jump.
  • Dynamic type 200%: key mono strings may overflow — wrap with ellipsis + full value on detail; rows reflow.
  • Reduced motion: no row stagger; state changes = color+icon+text (09 §8 of shared).

5. Interrupted sessions & retries

  • App killed mid-toggle: optimistic state lost; on restart list refetches → server truth.
  • Server 5xx on save: form retained, snackbar with requestId; retry safe (upsert idempotent).
  • 429 (api tier 100/min, rate-limit.constants.ts:6): countdown; no auto-retry (00-shared/07 §4).
  • Offline: flags list from last-good cache + offline banner; all writes blocked (no offline queue for flags); switches disabled.

6. Abandonment

  • Editor dirty → discard confirm; create abandoned → nothing created (no server touch).
  • Bulk sheet dismissed mid-apply → apply continues server-side; on reopen the list refetch reflects final state; client tracks last-known partial result for "retry failed".

7. Frustration points → UX treatments (summary)

FrustrationTreatment
"I toggled it but the app still shows it"propagation banner + pull-to-refresh + client TTL explainer (Settings → data freshness line, (proposed))
"It saved but my description is gone"editor warning banner; field marked read-only
"Bulk said done but two rows didn't change"per-row results with reasons; retry-failed button
"I deleted and now can't re-create"delete dialog warning; error copy "key slot retained — ask platform team or use a new key"
"I can see the toggle in someone else's school"impossible — tenant-scoped reads (base.repository.ts:20-30); QA check
Non-admin sees admin UIpermission-gated routes (target contract OQ-5)

8. Cross-device & conflict

  • Two admins toggle the same flag concurrently: last write wins (Mongo findOneAndUpdate, feature-flag.repository.ts:38-42); version field (base.schema.ts:30-31) is not checked in this module's repo — no optimistic locking (OQ-8: acceptable for toggles?).
  • Admin A deletes while admin B edits → B's save upserts a new doc for the key (or E11000 — OQ-4); UI shows 404-first → banner + reopen.
  • Client gating on device 1 vs 2 skews within client TTL — expected, documented in copy.

9. Adoption path

P0 (admin): list, single toggle, create/edit (key/enabled/label only — honest about the description/module caveat), delete with warning. P1: bulk, module filtering, enabled tab, affected-features map (proposed), org overlay merge UI. P2 (planned): server permission enforcement, 30 s cache + invalidation events (OQ-7), gradual rollout fields (OQ-6), institution-type matrices (IMPLEMENTATION_PLAN.md:310-336).