What is a feature flag, and how does using one to gate a new code path let a team deploy code to production without releasing it to users yet?
answer
- deploy != release
- dark launch
- trunk-based development enabler
- runtime conditional outside binary
- flip not redeploy
basics
~20 sA feature flag is an on/off switch in code that lets you turn a feature on for some or all users without redeploying. It separates 'the code is live on servers' from 'users can see/use it.'
solid answer
~30 sA feature flag is a runtime conditional (if flagIsOn(...)) wrapping a code path, controlled by a config value outside the binary. Deploying ships the code dark (flag off); releasing flips the flag on for chosen users, without a redeploy. This decouples deploy cadence (continuous, low-risk, reversible via rollback) from release cadence (business-controlled, targeted, instantly reversible via flag flip). It enables trunk-based development: merge incomplete work behind a flag instead of long-lived branches, avoid painful merge conflicts, and let ops kill a broken feature in seconds instead of waiting on a new deploy/rollback cycle.
go deeper
Should describe the basic mechanism (an if/else driven by an external switch) and say in plain terms that it lets you ship code without showing it to users yet.
Should articulate the deploy/release split explicitly, mention trunk-based development as the motivating workflow, and know that flags need to be removed eventually.
Should discuss the cost side unprompted -- code complexity from combinatorial states, testing burden, evaluation-consistency risk within a session -- not just the benefits.
Should connect flag practice to organizational velocity (why large orgs standardized on it) and articulate flag lifecycle as a first-class engineering process with ownership and expiry, not just a feature.
## What a feature flag is, mechanically A feature flag (also called a feature toggle) is a piece of runtime configuration -- typically a boolean or a multi-variant value stored outside the compiled binary, in a config service, database row, or file -- that a running program reads to decide which code path to execute. Mechanically the pattern is simple: instead of shipping code that always runs the new logic, the code becomes a conditional such as `if (flags.isEnabled("new-checkout")) { newCheckoutFlow() } else { oldCheckoutFlow() }`. - **Both paths ship together.** The old and the new code paths are compiled into the same artifact and deployed together. - **The flag-management system decides.** A flag-management system -- an in-process SDK talking to a flag service, or something as simple as an environment variable or a database row read on each request -- tells the running process, at evaluation time, which branch to take. - **Flipping takes effect within seconds.** Flipping the flag from off to on requires no new deploy, no new build, and -- because the SDK typically polls or streams updates -- takes effect within seconds across the fleet. ## Why the pattern exists: deploy is not release The reason this pattern exists is to break the coupling between two activities that traditional deployment conflates: **deploy** (get new code running on production infrastructure) and **release** (make a capability visible and usable to real users). Without flags those two events are the same act -- merging and shipping code IS exposing it. That forces teams into two bad habits: 1. They hold back a feature branch for weeks until it is fully done, fighting merge conflicts, drifting from main, blocking other changes that touch the same files. 2. They ship half-finished work live and hope nobody notices. Flags let engineers merge continuously to a trunk branch -- the core practice of **trunk-based development** -- landing incomplete or risky code behind a flag that defaults to off. The code is *dark launched*: it runs in production, exercised by CI/CD or even synthetic traffic, but real users never see it until someone with release authority, often a product manager rather than an engineer, flips the switch, independent of any deploy pipeline. ## The trade-off The trade-off cuts on both sides. On the benefit side: - Releases become instant and instantly reversible: flip the flag back off in seconds, versus a code rollback plus redeploy that can take minutes and re-introduces deploy risk. - Releases can be targeted -- turn a feature on for five percent of users, for internal staff only, for one customer's account -- instead of all-or-nothing. - Deploy risk is decoupled from release risk, so "ship early, release later" becomes normal. On the cost side: - Every flag is a second code path that must be tested, and a codebase that grows flags without discipline accumulates complexity: nested conditionals across flags produce a combinatorial set of states that are, in practice, never all tested. - Flags also introduce a new runtime dependency, the flag-evaluation call itself. If it is a network call on every request that adds latency and a new failure mode; if it is a local cached SDK, it adds a small memory and CPU footprint plus a staleness window between someone flipping the flag and a given server instance seeing the update. ## Failure modes Failure modes show up in a few characteristic ways. 1. **Flag debt**, the most common in production: nobody deletes the flag once the feature is fully rolled out and stable, so the codebase keeps carrying a dead conditional and a dead old-code-path indefinitely. 2. **Another failure mode, evaluation inconsistency**: if flag state is read fresh per-request without any pinning, the same user can see the old and new UI on different clicks within one session, which is confusing and can break stateful flows such as starting a checkout with the old flow's fields and submitting to the new flow's handler. 3. **A third** is the flag service itself being unavailable or slow -- does the SDK serve last-known-good state, or block? ## Where it shows up in the wild A concrete, well-known real-world example: LaunchDarkly (and similarly Unleash, Flagsmith, or Split) is built specifically around this deploy/release split -- teams merge to main continuously, deploy on every merge via CI/CD, and product owners control the actual customer-facing rollout from a dashboard, entirely decoupled from when engineering shipped the code. Netflix and Facebook popularized this pattern at scale specifically to let thousands of engineers ship to a shared trunk daily without every commit being an instant, all-or-nothing customer-facing event.
- If a team never removes feature flags after a feature is fully rolled out, what specifically goes wrong in the codebase over time?Each dead flag leaves a permanent conditional branch that must still be read, tested, and reasoned about even though only one branch ever executes; with dozens of stale flags the number of theoretically possible code-path combinations explodes even though almost none are exercised, so tests and code review both slow down and confidence in 'that path is truly dead' erodes. It also makes the code harder to read since new engineers can't tell which branches are still in flight versus permanent.
- How is 'deploy' different from 'release' in this model, concretely, in terms of who controls each and how they're reversed?Deploy is an engineering action, gated by CI/CD and code review, and reversed with a rollback/redeploy of the binary. Release is a product/business action, gated by whoever owns the flag dashboard, and reversed instantly by flipping the flag back off with no redeploy needed at all. Separating them means a broken release can be undone in seconds, while deploys can stay frequent and low-drama because they no longer equal going live.
- Why does dark-launching new code (deployed, flag off) still carry any production risk at all if users can't see it?The new code still runs -- for example background jobs, schema migrations it triggers, or code paths hit by internal or synthetic traffic -- so it can still consume resources, throw exceptions into logs and alerts, or contend for locks and connections even while flagged off for real users. A completely inert flag is only truly zero-risk if the conditional short-circuits before touching any shared resource.
Like a light switch wired to a bulb that's already installed and wired up: installing the bulb (deploying) and turning the room's light on for guests (releasing) are two separate, independently reversible actions.
saying these in an interview costs you the question
- Says a feature flag and an environment variable/config value are the same thing with no distinction from deploy/release
- Thinks flipping a flag requires a new deployment
- Can't explain who typically controls the flag flip (product) versus who deploys the code (engineering)
- Assumes flags have no ongoing maintenance cost once shipped
- Conflates 'the code is running' with 'users can see it'