Bell Statistics

What is a feature flag?

A feature flag is a runtime switch that decides which users see which code path, without a deploy. It is the mechanism experiments are delivered through, and the same infrastructure serves gradual rollouts and emergency shut-offs.

Also called
feature toggle, flag, release toggle, kill switch
Allon Korem

Written by Allon Korem

Chief Executive Officer

Last updated

In plain English

A feature flag separates deploying code from releasing behaviour. The new path ships to production disabled, and a runtime configuration decides who sees it — everyone, nobody, 10% of users, or one arm of an experiment. That separation is what makes continuous deployment compatible with careful release, and it is the delivery layer nearly every experimentation platform is built on.

Three distinct jobs run on the same infrastructure and they are worth keeping separate in your head. A release flag rolls something out gradually, watching error rates as the percentage climbs — that is risk management, not measurement, and it has no control group. An experiment flag assigns users randomly and holds the assignment stable for the duration, which is what makes a comparison valid. A kill switch exists to turn something off immediately when it misbehaves. Conflating the first two is the common error: a gradual rollout tells you the system did not fall over, and nothing about whether the change was any good.

For experimentation the requirements on the flag are specific. Assignment must be deterministic — the same user gets the same variant on every request and every device, or their experience is inconsistent and their data uninterpretable. It must be based on a stable identifier rather than a session, and it must be recorded, since an experiment whose assignments were not logged cannot be analysed at all.

The mistake that does the most damage is changing a flag's configuration mid-experiment. Increasing the rollout from 10% to 50% halfway through means the later users were assigned under different conditions and joined at a different point in the calendar, so the two arms are no longer comparable — this is a common cause of sample ratio mismatch, and it usually invalidates everything collected before the change. If the allocation must change, the honest response is to restart the experiment.

There is also an operational cost that experimentation teams inherit. Flags accumulate: every one is a branch in the code, and a codebase with hundreds of stale flags has an untestable number of possible states. Flags that outlive their purpose should be removed and the losing branch deleted, which sounds obvious and is one of the most consistently neglected pieces of hygiene in experimentation-heavy organisations.

The formula

Nothing here is statistical. What matters is the assignment function, which must be deterministic, and the failure mode when it changes mid-flight.

Deterministic assignment
variant = hash( user_id + experiment_id ) mod 100 < allocation

Same input, same output, on every request and device. Including the experiment id keeps concurrent tests independent.

Why the experiment id is in the hash
without it, every test assigns the same users to the same arm

Correlated assignment across experiments, which makes interactions systematic rather than random — see interaction effect.

What a mid-test change does
arms differ in join date as well as in treatment

Time becomes a confounder and the split usually fails a chi-square SRM check — see the chi-square calculator.

Rollout is not measurement
gradual release has no control group

It answers whether the system stayed up, not whether the change helped.

Worked example

A team launches an experiment at a 10% allocation to watch for errors, sees none after two days, and raises it to 50% to finish faster. The test runs a further twelve days and reports a 4.1% lift on conversion. A reviewer runs a sample ratio check before it ships.

Days 1-2
10% treatment, 10% control, 80% untracked
Days 3-14
50% treatment, 50% control
Total control users
184,200
Total treatment users
197,400
Expected split
50/50
SRM chi-square
462.3, p < 10⁻¹⁰⁰

The reported 4.1% lift cannot be trusted. The arms differ by 13,200 users, which no random split would produce.

The imbalance comes from the reallocation rather than from any bug in the assignment layer. When the percentages changed, the hashing boundaries moved, so some users who had been in control on day two found themselves in treatment on day three — and those users carry two days of control behaviour into the treatment arm's data. The arms now differ in composition and in when their members joined, and there is no analysis that separates that from the treatment effect. Two things to take from it. The instinct behind the change was sound: starting small to watch for errors is good practice. The error was doing it inside a running experiment rather than as a separate release phase that concludes before the experiment starts. And the SRM check is what caught it — a 3.4% imbalance is invisible by eye and overwhelming statistically, which is exactly why that check should run automatically on every experiment rather than when someone thinks to look.

Common misconceptions

Rolling a feature out gradually is a form of testing it.
It is risk management. A staged rollout tells you the system did not break as load increased, and it has no control group, so it cannot tell you whether the change helped. Measuring impact needs a concurrent randomised comparison; the two activities use the same flag infrastructure and answer different questions.
Increasing the allocation mid-test just gets you to significance faster.
It changes which users are in which arm, so the arms differ in composition and in join date as well as in treatment. This is a common cause of sample ratio mismatch and usually invalidates the data collected beforehand. If the allocation must change, restart the experiment.
Once a feature is fully rolled out the flag can stay for safety.
Every flag left in place is a branch that must keep working, and a codebase with hundreds of them has a number of possible states nobody can test. Keep a kill switch deliberately and briefly if the change is risky; remove the flag and delete the losing branch once it has proven itself.

Frequently asked questions

Is a feature flag the same as an experiment?
No — a flag is the delivery mechanism an experiment runs on. An experiment additionally requires random assignment, a concurrent control group, a pre-specified metric and a fixed duration. Flags can also serve gradual rollouts and kill switches, neither of which involves measurement. Treating a percentage rollout as an experiment is the most common conflation and produces confident conclusions from a design with no baseline.
Why does flag assignment need to be deterministic?
So that a user sees the same variant on every request, every session and every device. Non-deterministic assignment gives users an inconsistent experience — a feature that appears and disappears — and makes their data uninterpretable, since they contributed to both arms. Hashing a stable user identifier together with the experiment id gives determinism and keeps concurrent experiments independent of each other.
Can I change the allocation while an experiment is running?
Not without restarting it. Changing the percentages moves the hashing boundaries, so some users switch arms mid-flight and carry their prior behaviour with them, and the arms end up differing in join date as well as treatment. The resulting imbalance normally shows up as a sample ratio mismatch. Do the risk-management rollout first, let it conclude, then start the experiment at its final allocation.

Related terms

  • A/B/n test

    Several complete alternatives against one control — and each extra arm costs twice: less traffic and another chance to be wrong.

  • Randomization

    The one mechanism that makes a comparison causal — and the four ways it silently fails.

  • Switchback test

    Randomise the clock instead of the users — the answer when everyone shares one supply pool.

  • Treatment group

    The users who get the change — and the counting rule that decides whether the comparison is still randomised.

  • Hash-based assignment

    Compute the variant instead of storing it — stateless, consistent everywhere, and free of a lookup on every request.

Calculate it

  • A/B test sample size

    Size a two-proportion experiment before you launch, then read the lift and its interval once it lands.

  • Chi-square test

    Test a contingency table of counts for association — any number of rows and columns.

Knowing the term is the easy part

Applying it to a live measurement problem is the part that goes wrong. If you are designing an experiment, reading a result you do not trust, or trying to work out what your marketing actually caused, that is the work we do.

References