Feature flags

Per-user and per-role toggles with usage tracking, managed from the admin panel.

How evaluation works

Flags are evaluated server-side in priority order:

  1. User override: a flag set for this specific user wins
  2. Role override: otherwise, an override for the user's role applies
  3. Global state: otherwise, the flag's global enabled state

Unknown flag keys evaluate to false, so referencing a flag before creating it is safe.

Using a flag

Client-side, the hook handles the query and defaults to off while loading:

const showNewDashboard = useFeatureFlag('new-dashboard');

Server-side, call evaluateFlag(key, userId, role) from src/domain/features/evaluate.ts.

Managing flags

Admins manage everything at /admin/flags:

  • Create flags with a key (lowercase, numbers, hyphens, underscores), name, and description
  • Toggle global state
  • Add per-user or per-role overrides
  • View usage trends over time

User-specific overrides are also visible from the user detail page at /admin/users/:id.

Usage tracking

Track feature engagement with the trackFeature mutation, which upserts a daily count per user per flag. The admin flag detail view charts usage over a configurable window, so you can see whether an experiment is landing before you commit to it.

Patterns that work

  • Gradual rollout: ship dark, enable for your own user, then a role, then globally
  • Kill switch: wrap risky integrations so you can disable them without a deploy
  • Plan gating: combine role overrides with billing to gate features by tier