Marcelo Pastorino, Software Developer

Feature Flags

Controlling feature exposure at runtime so teams can deploy code separately from release and risk.

A feature flag (or feature toggle) is a runtime switch that controls whether a code path is active for a given user, tenant, or environment. The code ships in the build, but exposure is decided later without a new deploy.

Feature flags separate deployment from release. Deployment puts code in production. Release decides who sees the behavior.

Why teams use them

Goal What the flag enables
Safer rollout Turn a feature on for internal users, then a percentage, then everyone
Fast rollback Disable bad behavior in seconds without redeploying
Experimentation Compare variants without maintaining long-lived branches
Long-lived integration Merge incomplete work behind a default-off flag
Operational control Kill switches for risky integrations or expensive paths

CI/CD pipelines still build and promote artifacts. Feature flags change how much of that artifact is visible after it reaches production.

Deploy code, control exposure with flags

Common flag types

  • Release flag, gates a new feature until the team is confident. Remove after full rollout.
  • Ops flag, kill switch for load, dependency failure, or incident response. May stay long-lived.
  • Experiment flag, routes users to variants for A/B tests. Needs measurement and an end date.
  • Permission flag, exposes capability to specific tenants, plans, or roles.

Treat release and experiment flags as temporary. Permanent if (flag) branches become hidden configuration debt.

Rollout patterns

Feature flags complement deployment strategies from CI/CD:

Approach What moves Best fit
Canary deploy Traffic to a new binary Infra or runtime version changes
Feature flag Exposure inside one binary Product behavior behind existing deploy
Blue-green Whole environment swap Fast binary rollback
Flag percentage rollout User subset sees new logic Gradual feature release with observability

A canary tests a new build. A flag tests new behavior inside a build that is already live. Teams often combine both on risky launches.

Implementation basics

Flags need a consistent evaluation path:

  • Central evaluation, app asks a flag service or config store instead of scattering env vars.
  • Stable targeting, user ID, tenant ID, or session key should get the same result across requests.
  • Defaults, fail safe when the flag provider is unavailable. Usually default to off for new features.
  • Auditability, record who changed a flag, when, and what percentage applied.
async function createInvoice(user, cart) {
  const useNewPricing = await flags.isEnabled("new-pricing-engine", {
    userId: user.id,
    tenantId: user.tenantId,
  });

  if (useNewPricing) {
    return newPricingService.createInvoice(cart);
  }

  return legacyPricingService.createInvoice(cart);
}
async function createInvoice(user: User, cart: Cart) {
  const useNewPricing = await flags.isEnabled("new-pricing-engine", {
    userId: user.id,
    tenantId: user.tenantId,
  });

  if (useNewPricing) {
    return newPricingService.createInvoice(cart);
  }

  return legacyPricingService.createInvoice(cart);
}

Keep flag checks at clear boundaries such as service entry points or route handlers. Deep nesting across many files makes removal harder.

Observability and rollout discipline

Flag rollouts need the same evidence as deploys. Use observability to compare error rate, latency, saturation, and business metrics between enabled and disabled cohorts.

Practical habits:

  • Define success metrics before enabling the flag for real users.
  • Roll out in stages, internal, small percentage, wider audience.
  • Watch support volume and downstream side effects, not only HTTP 500s.
  • Keep a documented rollback step, usually set flag to off.
  • Remove the flag and dead code after the feature is fully released.

A flag without monitoring is just a hidden deploy with extra steps.

Lifecycle and cleanup

Every flag should have an owner and a expected lifetime.

  1. Create with default off and a short description of risk.
  2. Test in staging and with internal accounts.
  3. Roll out gradually with metrics attached.
  4. Make the feature generally available.
  5. Delete the flag and simplify the code path.

Old flags confuse readers and increase test surface. Schedule cleanup like any other production change.

Trade-offs

  • Flags add branching, testing combinations, and dependency on a flag service.
  • Long-lived flags make code harder to reason about and can hide unfinished design.
  • Misconfigured targeting can expose features to the wrong tenants.
  • Experiment flags need statistical discipline, not only engineering toggles.
  • They reduce redeploy rollback pressure but do not replace good CI/CD or infrastructure as code practices.

Feature flags are most valuable when exposure is intentional, measured, and temporary. Deploy continuously, release deliberately, and remove flags once the decision is made.

See also

  • CI/CD Pipelines, Automating build, test, and deploy with pipelines that scale with the team.
  • Observability, Understanding production behavior from logs, metrics, and traces.
  • Infrastructure as Code, Defining environments declaratively for repeatability and review.

On Wikipedia