Engineering6 min read

Feature Flags & Experimentation

Use feature flags for safe deploys, gradual rollouts, A/B tests, and kill switches using OpenFeature, LaunchDarkly, or self-hosted Unleash.

feature-flagsexperimentationa-b-testingdeployment

Feature flags decouple code deployment from feature release. They enable gradual rollouts (release to 5% of users before 100%), A/B tests, kill switches for instant disable, and per-user or per-tenant feature targeting-all without code changes.

Use the OpenFeature standard for vendor independence. Ship flags with every new feature. Evaluate flags server-side for consistent bucketing. Clean up flags after full rollout - stale flags become technical debt.

Workflow

  1. Set up OpenFeature with a providerOpenFeature is the CNCF standard - switching providers (LaunchDarkly, Unleash, Flagsmith) requires only changing the provider, not your code.
  2. Implement gradual rolloutReleasing to 100% immediately risks a bad experience for all users; gradual rollout limits blast radius.
  3. Run A/B tests with flag variantsA/B tests determine which variation of a feature performs better before fully committing.
  4. Implement kill switchesWhen a feature causes issues in production, instant disable without a deployment can prevent extended outages.
  5. Clean up stale flagsFlags accumulate and become hard-coded assumptions - stale flags are a maintenance burden and source of confusion.

Output Format

OpenFeature client integrated, flags wrapped around all new features, gradual rollout configured, A/B test logging, kill switch tested, cleanup schedule tracked.

Examples

Flag evaluation: `const variant = await client.getStringValue("checkout-experiment", "control", { targetingKey: req.user.id, attributes: { plan: req.user.plan, country: req.user.country } })`
Gradual rollout rule: `{ "targetingRules": [{ "query": "user.id % 100 < 20", "variant": "treatment" }] }` - 20% of users see treatment
Kill switch: `const useNewSearch = await flags.getBooleanValue("search-v2", true); const results = useNewSearch ? await newSearch(q) : await legacySearch(q)`

Edge Cases & Gotchas

  • Flag evaluation in SSR: evaluate server-side for consistent experiences; avoid client-side evaluation for critical UI paths to prevent flash of different content
  • Bootstrap consistency: pass evaluated flags to the client from SSR to prevent the client re-evaluating (and potentially getting a different value) on hydration
  • Flag dependencies: avoid flags that depend on other flags - creates evaluation order complexity and makes rollbacks complicated

When to Break These Rules

  • For simple boolean configuration that never changes after deploy (feature permanently enabled), environment variables are simpler than a flag service.

Reference Files

  • OpenFeature spec: https://openfeature.dev/
  • LaunchDarkly: https://launchdarkly.com/docs/
  • Unleash (self-hosted): https://docs.getunleash.io/
  • Flagsmith: https://docs.flagsmith.com/

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.