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

Building software · 5 min read ·

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.

A 10 by 10 grid of dark squares with ten lit green, showing a feature switched on for 10 percent of users

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.

$ ls software-design/

see all →
software-design/composition-vs-inheritance-why-a-toy-duck-shouldn-t-learn-to-fly.md Composition vs inheritance: why a toy duck shouldn't learn to fly A rubber duck that learns to fly is a classic sign that inheritance, the "is a" way of sharing code, has been stretched too far. Every… 5 min read · 30 Oct software-design/the-observer-pattern-how-a-shop-tells-many-functions-about-one-order-w.md The observer pattern: how a shop tells many functions about one order without knowing them A shop that must email a receipt, update stock and add loyalty points ends up with one function that knows about everything. Every new… 5 min read · 27 Oct software-design/refactoring-how-to-tidy-working-code-without-breaking-it.md Refactoring: how to tidy working code without breaking it Refactoring means changing how code is built on the inside without changing what it does. It sounds risky, but there is a simple routine… 6 min read · 24 Oct software-design/unit-tests-why-four-passing-tests-don-t-prove-a-function-works.md Unit tests: why four passing tests don't prove a function works Your function gave the right answer four times. Is it correct? Not necessarily: passing tests only show the bug isn't where you looked… 4 min read · 21 Oct software-design/idempotency-why-clicking-pay-twice-should-charge-you-once.md Idempotency: why clicking Pay twice should charge you once You tap Pay, the screen shows an error, you tap again, and your card is charged twice. Networks drop messages, so a phone often cannot tell… 6 min read · 18 Oct software-design/state-machines-why-an-order-can-t-be-delivered-before-it-s-paid.md State machines: why an order can't be delivered before it's paid An online order can be in the cart, paid, shipped, delivered or cancelled, but what stops the code from delivering an order nobody paid… 4 min read · 15 Oct