Architecture

Feature Flags

Decoupling deployment from release by toggling features at runtime without a redeploy.

Overview

Feature flags (also called feature toggles) decouple software deployment from feature release. New code is deployed to production but kept inactive until a flag is enabled. Flags allow gradual rollouts, A/B tests, instant rollbacks without redeployment, and targeting specific users or environments.

Origin

Feature flags became prominent through continuous delivery practices popularised by Jez Humble and David Farley in "Continuous Delivery" (2010). Services like LaunchDarkly (founded 2014) built businesses around flag management infrastructure.

Examples

Flag-gated feature rollout

import { FeatureFlags } from './flags'

// Simple boolean flag
if (FeatureFlags.enabled('new-checkout-flow', { user })) {
  return renderNewCheckout(cart)
} else {
  return renderLegacyCheckout(cart)
}

// Percentage rollout (stored in config or LaunchDarkly)
class FeatureFlags {
  static enabled(flag, context = {}) {
    const config = this.getConfig(flag)
    if (!config || !config.enabled) return false

    // Percentage rollout: hash userId for consistent assignment
    if (config.rolloutPercent < 100) {
      const hash = murmurhash(context.user?.id + flag) % 100
      return hash < config.rolloutPercent
    }
    return true
  }
}

// Usage: graduated rollout
// Week 1: rolloutPercent: 5    (5% of users)
// Week 2: rolloutPercent: 25
// Week 3: rolloutPercent: 100  (everyone)
// Remove flag from code once stable

Use Cases

  • 01Gradual rollout: expose a new feature to 1%, then 10%, then 100% of users
  • 02Kill switches: instantly disable a broken feature without a deploy
  • 03A/B testing: different users see different implementations for experimentation
  • 04Beta programs: enable features for specific users or accounts only
  • 05Operational flags: enable debug logging or maintenance mode at runtime

When Not to Use

  • //Flags accumulate technical debt, every flag is a code branch that must be maintained. Remove flags once rollout completes
  • //Security-sensitive code should not be flag-gated, authentication and authorisation must always be on
  • //Complex flag interdependencies create combinatorial test cases that become unmanageable

Technical Notes

  • Flag debt is real: Netflix and Facebook have written about the maintenance cost of hundreds of long-lived flags. Treat flags like branches, merge and delete promptly
  • Use consistent hashing for percentage rollouts: the same user must always see the same variant to avoid flickering
  • Flags should be defined in one place (a service like LaunchDarkly or a config file) and never duplicated across codebases
  • Test both paths of every flag, the non-flag path must continue to work after the flag is removed