09 — User Behaviour (Feature Flags Module)
- 1. Expected behaviour (server-contract-driven)
- 2. Common mistakes & countermeasures
- 3. Power & new users
- 4. Mobile & a11y users
- 5. Interrupted sessions & retries
- 6. Abandonment
- 7. Frustration points → UX treatments (summary)
- 8. Cross-device & conflict
- 9. Adoption path
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()returnsflag?.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-flagsfull-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
| Mistake | Related | UX countermeasure |
|---|---|---|
| Admin flips a flag and expects it visible on a colleague's phone "right now" | propagation | info banner on toggle success (immediate today; "~30 s" once cache lands); pull-to-refresh affordance |
| Editing description/module and believing it saved | PUT /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 applied | sequential no-transaction bulk (feature-flags.service.ts:40-44) | per-row results + "retry failed" |
| Delete → re-create same key → 500 | unique index + soft delete (OQ-4) | delete dialog warning; error copy with cause hint |
| Org-level edit wiping sibling flags | full-replace $set (organizations.service.ts:149-151) | merge-before-PATCH; diff preview in the org settings UI (proposed) |
| Admin assumes permissions protect flags | none enforced (rbac.guard.ts:29) | UI gates affordances by permission anyway; server enforcement (planned) (OQ-5) |
| Typing keys with spaces/uppercase | no server pattern validation | client hint a-z 0-9 . _ - (proposed); trim on submit |
| Re-creating a key expecting a clean slate | upsert matches on {tenantId,key} — same doc re-enabled | editor 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
Nnew 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
monostrings may overflow — wrap with ellipsis + full value on detail; rows reflow. - Reduced motion: no row stagger; state changes = color+icon+text (
09 §8of 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)
| Frustration | Treatment |
|---|---|
| "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 UI | permission-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).