Feature Flags

2025-11-16

Feature flags, also called feature toggles, let us enable or disable features in a running application without deploying new code. They decouple deployment from release. We can deploy code that contains a new feature but keep it hidden behind a flag until we are ready to turn it on.

Basic Implementation

At its simplest, a feature flag is a conditional check:

if (featureFlags.isEnabled("new-checkout-flow")) {
  return <NewCheckout />;
} else {
  return <OldCheckout />;
}

The flags can be stored in a configuration file, a database, or a dedicated feature flag service. The value of each flag determines which code path is executed at runtime.

Types of Feature Flags

Not all feature flags serve the same purpose.

Release flags control the rollout of new features. They allow the team to merge code into the main branch and deploy it without exposing the feature to users. Once the feature is ready, the flag is turned on. If something goes wrong, the flag is turned off immediately without needing a rollback deployment.

Experiment flags are used for A/B testing. A percentage of users see the new variant while the rest see the existing one. By comparing metrics between the two groups, we can make data-driven decisions about whether to ship the feature.

Ops flags provide operational controls. For example, a flag that disables a non-critical feature during high load, or a flag that switches between a primary and fallback data source. These act as circuit breakers that the team can toggle without deploying code.

Permission flags gate features based on user attributes. Early access programs, beta features, or features restricted to certain plans are controlled by flags that check the user's role, subscription, or other attributes.

Feature Flag Services

For production use, a dedicated service like LaunchDarkly, Flagsmith, or Unleash provides a management interface, targeting rules, audit logs, and SDKs for various languages. They handle the complexity of evaluating flags across different environments and user segments.

The evaluation happens on the server or client side depending on the setup. Server-side evaluation is more secure because the flag configuration does not leave the server. Client-side evaluation provides faster responses since it does not require a network call for every check.

Lifecycle Management

Feature flags have a lifecycle. They are created, used, and eventually removed. The removal part is often neglected. Over time, flags that are no longer needed accumulate in the codebase, adding complexity and dead code paths. Treating flags as temporary by default and scheduling cleanup after the feature is fully rolled out keeps the codebase clean.

A flag that has been enabled for everyone for three months is no longer a flag. It is dead code wrapped in a conditional. Removing it is straightforward: delete the flag check, keep the enabled code path, and remove the disabled path.

Testing with Flags

Feature flags add another dimension to testing. We need to verify that the application works correctly with the flag on and off. In unit tests, we mock the flag values. In integration or end-to-end tests, we test both code paths to ensure neither is broken.

Ignoring the off state of a flag is a common oversight. If a flag is suddenly turned off in production and the old code path has not been tested recently, we might discover that it is broken only when it matters most.