← Blog

Feature flags vs feature toggles: the same thing

Feature flags and feature toggles are the same thing. Both names describe one mechanism: a condition in your code that decides at runtime which path runs, without a new deploy. No specification or tool draws a technical line between them, and the canonical reference on the subject puts both words in its own title.

The split is historical, not technical. The engineering literature settled on “toggle” and the commercial tools settled on “flag”, so a single codebase often ends up carrying both, plus “switch” and “flipper” from whichever era each service was written in. The cost is a search that misses. grep -rn featureFlag src/ finds half your branches and leaves the rest in production.

The distinction that carries weight is the type of switch, not the noun on it. This post covers where each name came from and what to standardize on.

Key Takeaways

  • “Feature flag” and “feature toggle” describe an identical mechanism. Pete Hodgson’s article on martinfowler.com is titled “Feature Toggles (aka Feature Flags)”, which is the industry answering the question in its own headline.
  • The split is by era and audience. Fowler’s bliki entry on toggles landed in 2010, Hodgson’s full article in 2017, and the CNCF’s OpenFeature specification standardized on “feature flag” for tooling.
  • What actually differs is the type, not the name: release, experiment, ops, and permission toggles vary on how long they live and who changes them.
  • Mixed vocabulary costs you search. Three words for one concept across 29 call sites means no single grep finds the branch you are about to delete.
  • The word carries no metadata either way. In dif the flag is a Markdown file where owner, surface, metrics, and created are fields, and dif validate fails the PR when one is missing.

Feature flags vs feature toggles: one mechanism, two names

A feature flag is a conditional that picks a code path at runtime. A feature toggle is a conditional that picks a code path at runtime. Swap the noun and nothing else moves:

if (featureFlags.searchV2) return <SearchV2 query={q} />;
if (featureToggles.searchV2) return <SearchV2 query={q} />;

The rule that resolves the switch for a given user is the same either way, usually a stable hash of the user ID mapped into buckets. The exposure event fires the same way, and the dead branch behind it rots at the same rate.

Both words also cover the same range of behavior: a boolean read from a config file, a 10% rollout keyed on user ID, a kill switch an on-call engineer trips at 3am. If you want the mechanism from the ground up, what feature flags are covers the call site, the assignment rule, and the removal problem in full.

Where the feature toggle name came from

The vocabulary tracks who was writing at the time.

Flickr’s engineering post “Flipping Out” went up in December 2009 and is the early public write-up of the practice: branch in code rather than in version control, deploy to production continuously, and keep unfinished work switched off. The team’s own word was “flipper”, and it outlived the post. The Ruby library named Flipper still ships under that noun.

Martin Fowler’s bliki entry on feature toggles followed in 2010, and Pete Hodgson’s full article on the same site, “Feature Toggles (aka Feature Flags)”, in 2017. That article carries the taxonomy most teams still use, so “toggle” is the word in engineering writing and conference talks.

The products picked the other one. LaunchDarkly, Optimizely, Split, and GrowthBook all brand around “feature flag”, and OpenFeature, the CNCF project standardizing evaluation APIs across vendors, uses “feature flag” throughout its specification. That is the closest thing to a standards body weighing in, and it binds the tooling only.

The literature says toggle and the tools say flag. Both have meant the same thing for over fifteen years, and neither side is dropping its word.

The four toggle types that actually differ

The distinction worth arguing about is not the noun. It is which of four jobs the switch is doing, because that decides how long it lives and who is allowed to change it. Hodgson’s taxonomy sorts them on exactly those two axes.

TypeWhat it doesTypical lifetimeWho changes it
Release toggleHides unfinished work so you can merge to main earlyDays to weeksThe engineer who added it
Experiment toggleHolds a split to measure a metricTwo weeks to a quarterThe PM or growth owner
Ops toggleKills an expensive or risky path under loadMonths, sometimes permanentOn-call
Permission toggleGates a feature to a plan, a beta list, or staffPermanent by designProduct

If you are writing the ticket, the row in this table is what matters, not the noun in the title. A release toggle with no removal date is a leak. A permission toggle is product logic and should never be treated as temporary. Getting those two confused is how new-checkout-v2 ends up at 100% for three years with a dead branch behind it.

Nobody has ever been paged because a teammate called it a toggle.

Pick one name and let grep enforce it

The practical cost of the ambiguity shows up the day you try to remove something. Run this in a repo that has been through three hiring waves:

$ grep -rn "featureFlag" src/ | wc -l
      14
$ grep -rn "featureToggle" src/ | wc -l
       6
$ grep -rn "isEnabled(" src/ | wc -l
       9

Twenty-nine branches, three vocabularies, and no single search that finds them all. The engineer cleaning up search-v2 greps for featureFlag, removes the call sites in the checkout code, ships, and leaves two live conditions in the billing service that answer to featureToggle.

Fixing it is a one-time chore, not a debate:

  1. Pick the word your tools already use. If you run a hosted platform or OpenFeature-compatible SDKs, that word is “flag”. If your team’s shared reading is Hodgson’s article, “toggle” is fine. Either is correct.
  2. Write it in the style guide next to the other settled calls, so the next PR does not relitigate it.
  3. Run one codemod across helpers, file names, and directory names, in a PR that changes nothing else.
  4. Name the switches themselves in kebab-case and keep the string identical to the file: search-v2, called as dif("search-v2"). One string, greppable from either end.

Step four is the one that survives. Consistent identifiers matter more than consistent nouns, because the identifier is what you search for when you are deciding whether a branch is safe to delete.

The name carries no metadata. The file does.

Neither word tells you who owns the switch, which type it is, what it was supposed to prove, or when it should be gone. In most tools that information lives in a dashboard field, a Notion page, or nobody’s head.

dif puts it in the same file as the switch. A flag and an experiment share one .md format, checked into the repo next to the code they gate:

---
id: search-v2
status: active
owner: ada@acme.dev
surface: search
hypothesis: >
  The new search backend holds results_clicked flat while cutting p95
  query latency. Ramping, not testing.
variants:
  - id: "off"
    weight: 90
  - id: "on"
    weight: 10
metrics:
  primary: results_clicked
  guardrails: [query_latency_p95]
created: 2026-09-14
---

## Brief

Ramp to 50% once query_latency_p95 holds for a week. Remove the flag and
the old backend in the same PR.

The call site reads the same string, so the grep problem above cannot start:

if (dif("search-v2") === "on") {
  return <SearchV2 query={q} />;
}
return <SearchLegacy query={q} />;

dif validate then enforces the parts a naming convention cannot. It checks that variant weights total 100 and that owner is a real email, and it scans your source for dif("...") calls to report flags nobody references any more:

$ dif validate
Checking dif/experiments/active/search-v2.md ... ok
Checking dif/surfaces/search.md ... ok

Scanning src/ for dif() call sites ... 12 calls, 1 orphan
  orphan: dif/experiments/active/hero-copy-b.md
    referenced by: none

1 warning. See https://dif.sh/docs/troubleshooting/ for diagnostic codes.

Run it in CI and an unowned or orphaned flag shows up in review instead of in an archaeology session eighteen months later. The dif CLI documents each check and its diagnostic code.

Changing a weight is a merge and a deploy, so if your build takes ten minutes, your worst-case kill is ten minutes. A dashboard toggle is a second. For an ops toggle on a payment path, use something built for sub-second kills on that one switch. For release and experiment toggles, where the failure mode is that some users see the new thing, a merge is fine, and the reason you rolled back sits in the git history rather than in someone’s memory of a 2am click.

FAQ

Is there a difference between a feature flag and a feature toggle?

No. They are two names for one mechanism: a runtime condition that selects a code path without a deploy. The difference is which community you learned the term from. Engineering writing leans “toggle” after Fowler’s site, and product tooling leans “flag” after the vendors and OpenFeature.

Why do some people say feature toggle?

Because Hodgson’s article on martinfowler.com, the piece that named the four types most teams still use, is titled “Feature Toggles (aka Feature Flags)“. Anyone who learned the concept from that article, a conference talk citing it, or a systems-design interview says toggle. It is the same switch.

What about feature switches, flippers, and gates?

Also the same. “Flipper” comes from Flickr’s 2009 write-up and the Ruby library named after it, “switch” and “gate” are ad hoc, and every one of them is a conditional resolved at runtime. Treat any of these words in an old service as an alias, not as a separate system to migrate.

Is a feature toggle the same as an A/B test?

Not quite. An A/B test is one type of toggle: weights held at a fixed split, with a hypothesis and a primary metric attached, ending in a recorded decision. A release toggle uses the same assignment machinery but ramps to 100% and gets deleted. Same mechanism, different intent and different ending.

What should I call feature flags in my codebase?

Pick one word, write it down, and stop there. Match whatever your SDK and docs already say, so the word in your helpers and the word in your tooling stay the same. Then spend the attention you saved on naming the individual switches consistently, since that string is what you grep when deciding whether a branch is safe to remove.

How do I keep old feature toggles from piling up?

Give every one an owner and a removal condition at the moment you add it, and check both automatically. A switch whose owner left and whose call sites are gone is invisible in a dashboard and obvious in a diff, which is the argument for keeping feature flags in your repo and validating them in CI.

Feature flags vs feature toggles: name it once

Feature flags vs feature toggles is a vocabulary question with no technical answer. Both words mean a runtime conditional around a code path. The naming split came from the literature and the vendors landing on different nouns, and it has stayed unresolved for fifteen years because nothing depends on it.

Three things do matter. The type of switch decides its lifetime and its owner, so sort every one into release, experiment, ops, or permission before you argue about the noun. Consistent identifiers beat consistent nouns, because the identifier is what you search. And neither word carries the metadata that matters: owner, type, and removal condition. That has to live somewhere a person can find it in a year.

That belongs next to the code it gates, in a file a reviewer can open. Install the CLI and scaffold a workspace:

npm install -g @dif.sh/cli
dif init

dif init writes a dif/ directory, a typed client, and one Markdown file per switch, whatever you call them. No account and no API key. The docs cover install and the format.