Feature Toggles

Origins

Feature toggles (also called feature flags) have been part of software engineering for decades — teams have always had ways to enable or disable code paths. The practice was formalized for modern web development by Pete Hodgson in a 2017 Martin Fowler article1 that named the categories of toggles in widespread use and articulated when each applies.

Toggles became essential infrastructure as trunk-based development grew in adoption. A team that merges incomplete code to trunk multiple times daily needs a way to keep that code from breaking the user experience. Toggles are that mechanism.

The Four Toggle Categories

Hodgson's taxonomy identifies four distinct uses, each with different lifespans and management needs:

1. Release Toggles

Short-lived flags that let incomplete features merge to trunk without being exposed. Once the feature is complete and rolled out, the toggle is removed within days or weeks.

Lifespan: days to weeks. Management: lightweight; remove aggressively.

2. Experiment Toggles

Toggles used to A/B test variants of a feature. Different users see different code paths; outcomes are measured; the toggle is resolved when the experiment ends.

Lifespan: as long as the experiment runs. Management: tied to experiment infrastructure; remove when experiment concludes.

3. Ops Toggles

Operational switches that let the team disable features or fall back to safer paths in production. Useful for circuit-breaking, throttling, or quickly disabling problematic features during incidents.

Lifespan: indefinite. Management: deliberate — ops toggles are runtime infrastructure with ongoing operational implications.

4. Permission Toggles

Long-lived toggles that grant access to specific user cohorts — beta users, premium tiers, internal staff. These are effectively part of the product's permission model.

Lifespan: indefinite. Management: treated as product configuration, not as temporary technical debt.

What Toggles Enable

  • Trunk-based development: incomplete code can merge to trunk without breaking the user experience.
  • Continuous deployment: code can ship to production behind off-by-default toggles before any user sees it.
  • Decoupled deploy from release: technical deployment becomes routine; product release becomes a separate decision.
  • Progressive rollout: features enabled gradually for percentages or cohorts of users, with the option to roll back instantly.
  • A/B testing: experiments shipped via toggle without separate deployment infrastructure.
  • Operational safety: features can be disabled at runtime during incidents without redeployment.

The Cost of Toggles

Every toggle is technical debt. The codebase contains conditional logic that exists only because of the toggle; the conditional paths have to be tested in both states; the system's behavior depends on configuration that isn't visible in the code alone.

The cost is acceptable for the temporary toggles (release, experiment) because they're removed soon. The cost is unbounded for the permanent toggles (ops, permission) because they accumulate without bound.

Teams that don't manage their toggles end up with codebases full of long-dead release flags, abandoned experiments still running, and a configuration surface so complex that nobody can predict what the system will do in any given combination. The practice has a hygiene requirement that many teams underestimate.

Managing Toggles Well

Name and classify every toggle

The four categories have different management rules. Track which category each toggle belongs to; review temporary toggles regularly to ensure they're being removed.

Set retirement dates for temporary toggles

A release toggle with an expected lifespan of two weeks gets a "remove by" date. If the date passes without removal, that's a signal to investigate — either the rollout stalled, or the toggle has quietly become permanent.

Treat ops and permission toggles as configuration

These are not temporary code paths; they're part of the system's runtime configuration. Document them, version them, monitor their use, and treat changes to them as production changes.

Limit conditional density

Code with five nested toggle checks is unmaintainable. Refactor conditional logic into named abstractions; remove old toggles before adding new ones in the same region.

Use a toggle service, not scattered config

Toggle state in environment variables, config files, and code comments scattered across the system is a maintenance disaster. Invest in a single source of truth — an internal service or a third-party tool (LaunchDarkly, Split, Unleash, etc.).

Common Pitfalls

  • Permanent release flags: a toggle meant to control a release rollout becomes permanent because nobody removed it. The code carries the toggle indefinitely.
  • Untested toggle combinations: with N toggles, there are 2^N possible states. Most combinations are never exercised. Production behavior in unusual combinations becomes a surprise.
  • Toggle as documentation: a toggle exists because someone wasn't sure whether the new behavior was correct. The toggle becomes the team's way of avoiding the decision.
  • Branched code paths instead of toggle logic: separate code files or modules per toggle state, requiring parallel maintenance. Use one path with clear conditional gating.
  • Operational ignorance: an operator disables a feature without knowing the downstream effects. Document operational toggles' impact in runbooks.
  • Tooling first: buying a feature-flag tool without building the discipline around removing flags. The tool amplifies the problem.

Coaching Tips

Classify Every Toggle

Release? Experiment? Ops? Permission? The category determines management. Untyped toggles accumulate as debt by default.

Set Retirement Dates

Release and experiment toggles get explicit removal dates at creation. Missing the date is a signal to investigate, not to extend.

Audit Quarterly

Review the full set of active toggles every quarter. Old release flags, abandoned experiments, and undocumented ops switches all surface in the audit.

Centralize Configuration

One source of truth for toggle state. Scattered config across env vars, files, and commits becomes unmaintainable.

Test Both States

For temporary toggles, run tests in both on and off states. Otherwise the toggle becomes a guess about what the unused path does.

Remove Aggressively

The discipline is removal, not creation. Make removing finished release toggles part of the definition of done for a rolled-out feature.

Summary

Feature toggles are foundational infrastructure for modern continuous delivery practices. Without them, trunk-based development forces a choice between long branches and broken user experiences. With them, the team can merge continuously, release deliberately, and operate safely — capabilities that compound across the rest of the engineering practice.

The hygiene cost is real and often underestimated. A team that adopts toggles without the discipline to remove them produces a codebase full of dead branches and a configuration space too large to reason about. The investment in tooling, naming, and retirement discipline is what separates teams that benefit from toggles from teams that drown in them.

Footnotes
  1. Hodgson, P. (2017). Feature Toggles (aka Feature Flags). martinfowler.com.
Back to Technical Practices