Deploying code and releasing a feature feel like the same event, but treating them as two separate decisions — deploy whenever a change is ready, release whenever the business is ready — is what lets a team ship to production multiple times a day without every merge being a high-stakes, all-or-nothing event.
4 min read
Deploying means new code is running in production. Releasing means users can actually see or use the new behavior. Most teams conflate the two — a deploy is a release, so every deploy is inherently risky, and a half-finished feature can't be merged until it's entirely done, because merging and shipping are the same act.
A feature flag decouples them: the code deploys behind a runtime toggle, running in production but inert until the flag is turned on for some or all users.
if (featureFlags.isEnabled("new-checkout-flow", { userId: user.id })) {
return renderNewCheckout();
}
return renderLegacyCheckout();The new checkout code can be merged, deployed, and sitting live in production — genuinely running, genuinely tested against real infrastructure — while being invisible to every real user until the flag says otherwise.
Trunk-based development means everyone commits directly to main (or merges very short-lived branches, hours not weeks) instead of working on long-lived feature branches that diverge from main for days or weeks. Long-lived branches create their own predictable pain: merge conflicts compound the longer a branch lives, and a giant merge at the end is exactly the kind of large, risky integration that Continuous Integration (covered in this domain's first lesson) exists to prevent in the first place.
Feature flags are what make trunk-based development practical for features that take longer than a day to build: instead of a long-lived branch hiding unfinished work from main until it's done, the unfinished work merges into main continuously, deployed continuously, hidden behind a flag that's off until the feature is actually ready — small, continuously-integrated commits, with a runtime toggle doing the job a branch used to do.
Treating a release flag like it's meant to live forever is where flag debt comes from — see below.
The moment a release flag reaches 100% rollout and stays there, the if (flag) { new } else { old } branch becomes dead weight — the "old" path is never taken but still compiles, still gets touched by refactors, still has to be reasoned about by anyone reading the function. A codebase with a year of undeleted, fully-rolled-out release flags accumulates permanent branching complexity for decisions that were already made and never revisited.
// Six months after 100% rollout, this should not still exist:
if (featureFlags.isEnabled("new-checkout-flow", { userId: user.id })) {
return renderNewCheckout();
}
return renderLegacyCheckout(); // dead code that nobody's deletedThe fix isn't a tooling fix — it's a process one: treat "delete the flag and the old code path" as part of the feature's actual definition of done, not an optional follow-up cleanup that competes for priority against new work and reliably loses. Some teams enforce this with a flag "age" dashboard that flags (no pun intended) anything at 100% rollout for more than N weeks as due for cleanup.
Feature flags interact directly with the deployment strategies covered later in this domain: a canary rollout gradually shifts traffic to a new version, while a feature flag gradually shifts visibility of a specific feature — often used together, where the underlying code ships via a normal (even instant, 100%) deploy, and the feature itself ramps up separately and on its own schedule, decoupled from any deploy risk entirely.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. What is the core distinction a feature flag introduces into the deploy process?
2. Why do feature flags make trunk-based development practical for features that take longer than a day to build?
3. What is a 'release flag' meant to be, in contrast to an ops/kill-switch flag?
4. Why does an old, fully-rolled-out release flag become a maintenance problem?
CI/CD & Deployment Pipelines