software-design/feature-flags-how-to-ship-code-switched-off-and-turn-it-on-for-10-of-u.md
Feature flags: how to ship code switched off and turn it on for 10% of users
A feature flag is a yes/no setting your code checks before choosing between old and new behaviour, so you can ship code switched off and turn it on later.
Drag a rollout slider over 100 users, see why a coin flip makes people flicker between versions, and how hashing a user id into a bucket from 0 to 99 keeps every answer stable. Includes a runnable JavaScript sample and the one rule that stops flags piling up.

You finished a new checkout page. It passes your tests, but you are nervous: what if it breaks for real customers? You could wait until you are sure, or you could ship the code switched off and turn it on slowly. The second idea is called a feature flag, and it is one of the most useful habits in software design.
A flag is just an if
A feature flag (also called a feature toggle) is a yes/no setting that your code checks before choosing what to do. The old code and the new code both live in the program; the flag decides which one runs.
if (isOn("new-checkout", user)) {
showNewCheckout();
} else {
showOldCheckout();
}
Where does isOn get its answer? Usually from a setting you can change without deploying new code: a config file, a database row, or a flag service. That is the whole trick. Deploying (putting code on the server) and releasing (letting users see it) become two separate steps, so you can deploy on Tuesday and release on Thursday.
Turning it on for some people
An on/off switch for everyone is useful, but the real power is a percentage rollout: show the new page to 10% of users first, watch for errors, then raise it. The obvious way is to flip a coin for each visitor. Try that below with the "Coin flip" button, then press "Reload page" a few times.
Why a coin flip is a bad idea
With the coin flip, the same person can see the new page, reload, and get the old one. Their shopping cart might look different on every click, and any bug report becomes impossible to reproduce. A fair rollout needs each person to get the same answer every time.
The fix is to give every user a fixed number from 0 to 99, called a bucket, and turn the feature on when the bucket is below the rollout percentage. To get a bucket you run the user's id through a hash function: a recipe that turns any text into a number, always the same number for the same text, but scrambled enough that similar ids land far apart.
function hash(text) {
let h = 0x811c9dc5; // FNV-1a, a small, well-known hash
for (const ch of text) {
h ^= ch.charCodeAt(0);
h = Math.imul(h, 0x01000193) >>> 0;
}
return h;
}
function bucket(flag, user) { return hash(flag + ":" + user) % 100; }
function isOn(flag, user, percent) { return bucket(flag, user) < percent; }
Running it for five people gives real, repeatable numbers:
bucket("new-checkout", "ana") // 60
bucket("new-checkout", "ben") // 3
bucket("new-checkout", "chloe") // 15
bucket("new-checkout", "dev") // 1
bucket("new-checkout", "eli") // 58
At 10%, only Ben and Dev (buckets 3 and 1) are in. Raise it to 25% and Chloe (15) joins. Ben and Dev stay in, because their numbers did not change; only the line moved. Go back to "Stable bucket" in the figure above and drag the slider slowly: squares turn green and never turn back.
Why the flag name goes into the hash
Notice flag + ":" + user. If the hash used only the user id, the same unlucky 10% would be first in line for every new feature, and those people would always be the guinea pigs. Mixing in the flag name gives each flag its own shuffle. In the example, Ana gets bucket 60 for new-checkout and 95 for dark-mode.
The hash spreads people roughly evenly, not exactly. Over 1,000 made-up users (user1 to user1000) at 10%, this code turned the flag on for 92 of them, not 100. Roughly 10% is good enough for a rollout.
The kill switch
The best moment to love flags is when something goes wrong. If the new checkout starts throwing errors, you do not need to rebuild and redeploy: you set the rollout to 0 and everyone is back on the old page in seconds (how quickly depends on how often your app re-reads the setting). Press "Kill switch" in the figure to see it.
The catch: flags are debt
Every flag doubles the paths through your code: with two flags there are four combinations, with ten there are 1,024. Nobody tests all of them. So treat a flag as temporary:
Give each one a name that says what it does, write down who owns it, and delete it (along with the old code path) once it reaches 100% and you are happy. Old flags nobody remembers are a classic source of confusing bugs.
What to take away
A feature flag lets you separate "the code is deployed" from "people can see it". Use a stable hash, not a coin flip, so each person gets a consistent answer; include the flag name so different features pick different people; keep a way to switch to 0 quickly; and clean up when you are done. You can try the idea in your own project today with a plain object: const flags = { "new-checkout": 10 }, and the three functions above.