awesome-everything RU
↑ Back to the climb

Engineering Practice

Feature flags decouple deploy from release

Crux Daily integration of long features needs deploy and release split apart. A flag ships unfinished work to production dark — integrated but unreachable — so release is a runtime ramp you can roll back in seconds. Flag types differ in lifetime: release, ops, experiment, permission.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at middle altitude — in the sky
◷ 16 min

“I can’t merge daily — my feature isn’t finished. I can’t put half a checkout flow on trunk.” This is the objection that sinks most trunk-based adoptions, and it rests on a hidden assumption: that putting code in production means exposing it to users. Separate those two and the objection dissolves. The half-finished checkout flow ships to production today, runs through CI, sits there fully integrated — and no user can reach it, because it’s behind a flag that’s off. You deployed the code. You didn’t release the feature.

Deploy and release are different events

Bundling “deploy” and “release” into one act is what forces long-lived branches: if shipping code is the same as exposing the feature, then incomplete work must stay off trunk until it’s done. Feature flags break the bundle. Deploying is moving code to production servers. Releasing is making a feature reachable by users. A flag is the runtime switch between them: wrap the new path in a conditional that reads a flag, ship it off, and the code is deployed, integrated, and tested but dark — present and unreachable.

This is what makes daily integration compatible with multi-week features. The work merges to trunk every day as small green commits, each hidden behind the off flag, so you never accumulate drift (lesson 1) and never need a long-lived branch (lesson 2). When the feature is complete, you don’t deploy anything — you flip a flag. Release stops being a risky big push and becomes a configuration change you can make at 10 a.m. on a Tuesday with the author watching dashboards.

Dark launch and progressive exposure

Once release is a runtime decision, you get capabilities a deploy can’t give you. Dark launching means running the new code path in production before showing its results to users — e.g. send real traffic to the new recommendation service, compare its output to the old one and measure latency, but discard the new output. You load-test and validate against production reality with zero user-visible risk. Then you progressively expose: turn the feature on for internal users, then 1%, then 5%, 25%, 100%, watching error rates and latency at each step. This is a canary by flag — blast radius grows only as confidence grows.

The same switch is your fastest recovery tool. If the ramped feature misbehaves at 5%, you flip the flag off — a kill switch — and every user is back on the old path in seconds, no rollback deploy, no revert PR, no waiting for CI. Decoupling deploy from release turns “we shipped a bug, start the incident” into “we turned it off, now let’s debug calmly.”

Flag typePurposeExpected lifetimeWho flips it
ReleaseHide unfinished work; ramp a rolloutDays to weeks — delete after 100%Dev / release owner
Ops / kill switchDisable a feature under load/incidentLong-lived by designOn-call / ops
ExperimentA/B test, measure a hypothesisOne experiment cycleProduct / data
Permission / entitlementGate features by plan/rolePermanent (it’s a product rule)Product / runtime

Not every flag is the same animal

The single word “flag” hides four very different things, and conflating them is how flag systems rot. A release flag is temporary scaffolding — it exists to hide unfinished work and ramp a rollout, and it must be deleted once the feature is at 100%. An ops flag (kill switch) is meant to live forever, so you can shed load or disable a misbehaving subsystem during an incident. An experiment flag lives for one A/B cycle. A permission flag is permanent because it encodes a product rule (this feature is for Pro plans). The dangerous category is the release flag, because it looks permanent if you forget it — and a forgotten release flag is the flag-debt problem of the next lesson.

Why this works

The reason “decouple deploy from release” is the unlock for trunk-based isn’t the flags themselves — it’s that it removes the only honest reason to keep work off trunk. Without flags, “my feature isn’t done” is a real argument for a long-lived branch. With flags, unfinished work has a safe home in production, so there’s no longer any excuse to let a branch accumulate drift. The flag is what makes “integrate daily” achievable for work that takes a month.

Pick the best fit

You're rolling out a rewritten search ranking that touches every query. How do you release it with the smallest blast radius?

Quiz

What hidden assumption makes 'I can't merge daily, my feature isn't done' feel true — and how does a flag dissolve it?

Quiz

Which flag type is temporary scaffolding that must be deleted after the feature reaches 100%?

Order the steps

Order a safe release of a risky feature using flags:

  1. 1 Merge the work to trunk daily behind a release flag that's off — deployed but dark
  2. 2 Dark launch: run the new path in prod, compare output, discard it
  3. 3 Enable for internal users, then ramp 1% → 25% → 100% watching metrics
  4. 4 Keep a kill switch ready to flip off in seconds if metrics regress
  5. 5 At stable 100%, delete the release flag and the dead old path
Recall before you leave
  1. 01
    Explain why decoupling deploy from release is the specific thing that makes trunk-based work for multi-week features.
  2. 02
    Beyond hiding unfinished work, what operational capabilities does a flag give you, and why does flag taxonomy matter?
Recap

Daily integration of multi-week work is only possible because deploying code and releasing a feature are separable events, and a feature flag is the runtime switch between them: wrap the new path in a conditional that’s off, and the code ships to production dark — integrated and tested but unreachable. That gives unfinished work a safe home on trunk so you never need a long-lived branch, and it turns release from a risky deploy into a configuration change. It also unlocks operational power: dark launching validates new code against real traffic with no user impact, progressive exposure ramps blast radius with confidence, and a kill switch makes recovery a seconds-long flip instead of a rollback. But “flag” conflates four animals with different lifetimes — temporary release flags, permanent ops kill switches, one-cycle experiment flags, and permanent permission flags — and the release flag is the one that must be deleted at 100%, or it quietly becomes the flag debt of the next lesson.

Connected lessons
Continue the climb ↑The green-trunk gate is the whole bargain
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.