In this article
  1. Before you start
  2. Why interviewers ask this
  3. Feature flag assignment hashing from first principles
  4. Worked example
  5. Trade-offs and edge cases
  6. How to answer it in an interview
  7. Common mistakes
  8. Follow-up questions
  9. Sources

Before you start

This page sits inside the distributed systems interview questions learning path. It assumes you have met a feature flag in a release process; the deployment strategies page covers why teams ship behind flags.

Why interviewers ask this

The question appears in design rounds for experimentation platforms, rollout systems and A/B testing services, and it is reported by candidates in system design interviews. It looks small, yet it hides a real design decision: how do you serve every request a consistent variant without a database lookup on every request?

A weak answer says "pick a random number and save it." That needs storage, a read on the hot path and a cleanup story. A strong answer hashes a stable key, explains why the result stays put, and names what would break it: a missing key, a changed salt or two software development kits (SDKs) that disagree on the arithmetic. A Staff-level answer adds how you would notice a broken assignment, through a sample ratio check, and why the check matters. Wish engineers state the requirements plainly: the algorithm must be fast, called hundreds of thousands of times per second, consistent for each user, and independent between experiments.

Feature flag assignment hashing from first principles

Bucket
A bucket is a numbered slot in a fixed range, such as 0 to 9,999, that a hash of a user's key lands in. The bucket never changes for the same inputs, and a rollout rule only decides which range of buckets sees which variant.
Salt
A salt is a string, unique to one flag or experiment, that is mixed into the hash input so that the same user lands in different buckets in different flags. In this setting it is a public namespace for independence, not a secret.

In plain words

Picture a coat check where every guest gets a ticket number from a fixed rule applied to their name, with no notebook. A guest who returns gets the same number, because the rule is the same. A rollout is then a sign saying "tickets 1 to 500 may enter" (illustrative). Raising the sign to "1 to 2,000" (illustrative) lets more guests in but never turns anyone away. The analogy breaks in one place. Real systems use a different rule per flag, so the same guest holds a different ticket number for each event.

Put precisely, assignment hashing computes a deterministic number from the flag and the user, then scales it to a bucket. The variation whose threshold range contains that bucket is served.

Step 1: Build the hash input from a stable key and a salt

The input to the hash has three parts: something that names the flag, something that names the user, and a separator. The user part is the targeting key. A targeting key is a string that uniquely identifies the subject of a flag evaluation; the OpenFeature specification defines it as an optional string field of the evaluation context, and notes that providers may behave unpredictably if it is missing for fractional evaluation.

LaunchDarkly documents the recipe: concatenate the flag's key, the flag's salt and the context's attribute value, joined with periods. Statsig generates a unique salt for each experiment or gate rule. The flagd project uses the flag key followed by the targeting key when no bucketing expression is given.

Input: a user key and a flag identity. Output: one string to hash. The salt is what keeps experiments independent, as the salt-overlap comparison in the worked example shows. You now know what goes into the hash; next it is turned into a bucket.

Step 2: Turn the hash into a bucket number

A hash function returns a large integer or a string of hexadecimal digits. The system folds it into a small fixed range. As of October 2026, LaunchDarkly takes the first 15 hexadecimal characters of the Secure Hash Algorithm 1 (SHA1) digest, parses them as a base-16 integer and divides by 0xFFFFFFFFFFFFFFF, which is 1152921504606846975, giving a bucket value between 0 and 1 (LaunchDarkly, flag evaluation rules). Statsig runs the user identifier through Secure Hash Algorithm 256 (SHA256) together with the salt and applies a modulus of 10000, or 1000 for layers (Statsig docs).

Input: the hash. Output: one bucket. The LaunchDarkly percentage rollout page describes 100,000 buckets numbered 1 to 100,000. A bucket space of this size gives fine control: one percent is a block of buckets, not a rounding guess.

Step 3: Compare the bucket with the rollout thresholds

The rollout is a list of variations with weights. The SDK starts at zero and keeps adding each weighted variation's weight divided by 100,000; when the bucket is less than the running sum, that variation is returned (LaunchDarkly). For a 50% allocation to variation A, buckets 1 through 50,000 get A (LaunchDarkly percentage rollouts).

Input: a bucket and the weights. Output: a variation. No table of users is read. Every server that runs the same arithmetic reaches the same answer, which is what the flagd documentation means when it says assignment is deterministic (sticky). Sticky assignment is the property that the same context value always resolves to the same variant (flagd specification).

Step 4: Ramp up by moving only the threshold

When you raise a rollout from a small share to a larger one, each user's bucket stays where it was. Only the line between the variations moves, so users already exposed stay exposed and the new users are added on top. That is why a ramp from 5% to 20% to 50% to 100% has nested exposed sets.1

Changing the percentage reassigns contexts according to their bucket's position in the range (LaunchDarkly). Change the salt, the bucketing attribute or the rule and everyone is reshuffled.

A ramp moves the threshold while each user's bucket stays putFour stages: the flag key, the salt and the user key form the hash input, the hash becomes a bucket, the bucket is compared with thresholds, and a ramp moves only the threshold.Key plus saltstable inputBucketfixed rangeThresholdsweightsRamp upthresholdmovesDiagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/feature-flag-experiment-assignment-hashing.html

A user's bucket never changes; only the threshold that decides the variant moves.CC BY 4.0 — source: How does feature flag experiment assignment hashing work?
Step-by-step description
  1. Flag key, salt and user key form the input.
  2. The hash becomes one bucket number.
  3. The bucket picks a variation.
  4. Only the threshold moves.
  • The hash input joins the flag key, the flag salt and the stable user key.
  • The hash is turned into one bucket number in a fixed range.
  • The bucket is compared with cumulative rollout thresholds.
  • Raising the percentage moves only the threshold, never the bucket.

The hash input joins the flag key, the flag salt and the stable user key. The hash is turned into one bucket number in a fixed range. The bucket is compared with cumulative rollout thresholds. Raising the percentage moves only the threshold, never the bucket.

Knowing it is half the job. Saying it is the other half.

Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.

Start Free Trial →

Worked example

Take an illustrative service with a synthetic population of 100,000 accounts and a bucket space of 10,000 buckets. The script uses a 32-bit MurmurHash3 written inside the script as the hash, with the input salt, a period and the user key.1 The vendors use other hashes, but the shape of the result does not depend on which one.

First, by hand with illustrative numbers on the LaunchDarkly scheme: variation A has weight 50,000 and B has weight 50,000, so the running sum after A is 50,000 divided by 100,000, which is 0.5. A user whose bucket value is 0.3721 is below 0.5 and gets A. A user at 0.8402 is not, and gets B. Raise A to 80,000 and the sum becomes 0.8, so the second user is still above it, but a user at 0.6 moves from B to A; nobody moves from A to B.

The ramp is nested, a shared salt makes two rollouts overlap completely, and a per-flag salt makes them nearly independent.
QuantityValue (expected; exact counts in the saved output)
Accounts hashed (illustrative)100,000
Buckets (illustrative)10,000
Accounts exposed at 5% (expected)about 5,000
Accounts exposed at 20% (expected)about 20,000
Accounts at 5% still exposed at 20%all of them
Overlap of two 10% rollouts, same key and no salt (true by construction)100%
Overlap of two 10% rollouts, one salt per flagabout 10%
Share of 50% group kept after a salt changeabout 50%

The counts behave as the arithmetic predicts.1 Without a salt, the first 10% of buckets for one flag is the same group of users as the first 10% for every other flag, so one cohort takes the risk of every new feature and its metrics are contaminated by all of them together. With a salt per flag, the overlap falls to about the share you would expect from independent draws, about 10%.1 When the salt changes, the 50% group is drawn again, and about half of the old group remains in the new one by chance, which is the same as a fresh random split.1

A salt per flag stops two rollouts sharing the same usersBar chart of how much the first 10% of two flags overlap in an illustrative run of 100,000 accounts: 100% when the hash ignores the flag, about 10% with a salt per flag.Overlap (%)050100No saltSalt per flagHash input10010Diagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/feature-flag-experiment-assignment-hashing.html
Without a per-flag salt the same users are first into every rollout.CC BY 4.0 — source: How does feature flag experiment assignment hashing work?
Text description of the diagram
  • Without a salt the first ten percent of two flags overlap completely.
  • With a salt per flag the overlap falls to about ten percent.

Without a salt the first ten percent of two flags overlap completely. With a salt per flag the overlap falls to about ten percent.

Checking the split with a chi-square test

Now suppose the assignment is wrong and you want to find out. The Kohavi slides (2017) give an example with 821,588 against 815,482 between the two variants, a 50.2/49.8 split against an expected 50/50. The p-value is 1.8e-6, which the slides describe as rarer than 1 in 500,000. Fabijan et al. (2019) say a chi-square test can detect a sample ratio mismatch (SRM), and that SRM in most cases completely invalidates experiment results.

By hand: the total is 1,637,070, so each side is expected to hold 818,535. Each side is off by 3,053, and the chi-square statistic is about 22.8.1 A split that looks "roughly 50/50" can still be a serious defect at this scale.

Trade-offs and edge cases

Vendors differ in details but share the structure. The table compares three documented designs; the sentence above each fact is sourced in the steps.

Vendors hash differently but all salt the flag into the inputTable comparing LaunchDarkly, Statsig and flagd by hash function, bucket space and the input that keeps flags independent.QuestionLaunchDarklyStatsigflagdHashSHA1SHA256Murmur3Bucket space100,00010000; 1000layers0 to 100(float)Independenceflag key andsaltsalt per ruleflag key ininputDiagram: Interview Clozo, CC BY 4.0 ·interview.clozo.ai/blog/feature-flag-experiment-assignment-hashing.html
Vendors choose different hashes, but each puts a flag-specific input into it.CC BY 4.0 — source: How does feature flag experiment assignment hashing work?
Text description of the diagram
  • LaunchDarkly hashes with SHA1, Statsig with SHA256 and flagd with MurmurHash3.
  • The Java and Python providers in that issue compute a float between 0 and 100.

As of October 2026, LaunchDarkly hashes with SHA1 and Statsig with SHA256, while flagd providers use MurmurHash3 (rust-sdk-contrib issue). The Java and Python providers in that issue compute a float between 0 and 100 with abs(hash) / (2**31 - 1) * 100. This suggests that the hash need not be cryptographic: uniformity matters, not secrecy. That is an inference from the vendor choices, not a documented rule.

Other edge cases a Staff answer names:

  • Missing key. If the attribute value is not found, LaunchDarkly sets the bucket to 0 (LaunchDarkly), so every keyless user falls into the first slot. Providers of other systems may behave unpredictably without a targeting key (OpenFeature).
  • Unstable identifiers. The assignment is only as stable as the key. An anonymous user who gets a new identifier on another device gets a new bucket.
  • Different SDK arithmetic. Every SDK must implement the same hash and the same division. A reported bug in a Rust flagd provider divided by the maximum of an unsigned integer where the other language providers use the signed maximum, so it produced different buckets for the same user (rust-sdk-contrib issue).
  • Deliberate salt sharing. Statsig lets you copy and set salts, for example for a series of related experiments that must reuse control and test buckets (Statsig docs). Independence is the default; sharing is an explicit choice.

Hashing here is not the consistent hashing of a cache ring. A ring spreads keys across servers and tolerates server changes; bucket hashing maps a key to a fixed range and fixes the rule, so the two solve different problems. For the ring, see consistent hashing and the distributed cache. For the cost side, the check runs on every request, so it belongs in your back-of-the-envelope estimates, and any extra network call would show up in tail latency; local hashing avoids that call.

How to answer it in an interview

Interviewer asks

How would you assign users to variants in an experiment, consistently, without storing every assignment?

A strong answer

I hash a stable user key together with the experiment's own salt, fold the hash into a fixed bucket range, and compare the bucket with the rollout thresholds. The same user always lands in the same bucket, so assignment is sticky without a table. Raising the percentage moves only the threshold, so exposed users stay exposed. The salt keeps experiments independent. What breaks it is a missing key, a changed salt or SDKs that disagree on the arithmetic, so I monitor the split with a chi-square check.

The spoken structure: say the recipe, say why it is sticky, say why the ramp is safe, then name the salt and the monitoring.

Common mistakes

  • "Assignment is random per request." (wrong) A random call flickers: the user may see both variants. (right) "I hash a stable key, so the answer is deterministic and sticky."
  • "Raising 10% to 50% reshuffles users." (wrong) Bucket positions are fixed. (right) "Only the threshold moves, so the first group stays exposed. Changing the salt, the attribute or the rule is what reshuffles."
  • "I hash the user identifier alone." (wrong) The same users are first into every rollout. (right) "I add the flag or experiment salt so each experiment draws independently."
  • "The hash must be cryptographic." (wrong) Vendors use both SHA256 and a non-cryptographic hash. (right) "I need a uniform, fast, identical hash on every SDK; security is not the goal."
  • "The salt is a secret, like in password storage." (wrong) Here it is a namespace and can sit in the flag configuration. (right) "The salt's job is independence, not secrecy."
  • "A 50.2/49.8 split is close enough." (wrong) At large scale it can be a significant sample ratio mismatch. (right) "I run a chi-square test on the split before reading any metric."

Follow-up questions

How do feature flags do percentage rollouts?

Feature flags do percentage rollouts by hashing the user key with the flag's key and salt into a bucket and serve the new behaviour to buckets below the threshold. Raising the percentage raises the threshold, so earlier users stay in. The deployment strategies page places this inside a release plan.

Is feature flag bucketing random?

It looks random across users but is deterministic for one user. The same inputs give the same bucket every time, which is what makes it sticky, and the hash spreads different users close to evenly.

What happens if you change the salt in the middle of an experiment?

Every user is drawn again, so users switch variants and the experiment's data from before and after no longer describe the same groups. Change a salt only to start a fresh experiment on purpose.

Does the hash have to be cryptographic?

No. It has to be uniform enough, fast and identical in every SDK. LaunchDarkly and Statsig use cryptographic hashes while flagd uses MurmurHash3, so the vendors disagree, which shows that security is not the requirement.

How do you know the assignment is broken?

Compare the observed user counts per variant with the planned split using a chi-square test. Kohavi, Tang and Xu (2020) treat sample ratio mismatch as a guardrail for trust.

Where does this show up in other design questions?

Anywhere a request needs a stable, stateless decision, such as sharding keys in a distributed rate limiter or the evaluation endpoint in application programming interface (API) design.

Key takeaways
  • Hash a stable key plus a per-flag salt, fold it into a fixed bucket range and compare it with thresholds.
  • The result is sticky with no assignment table, so a ramp only moves the threshold and exposed users stay exposed.
  • The salt, not secrecy, keeps experiments independent; changing it reshuffles everyone.
  • Every SDK must use the same hash and arithmetic, and a missing key breaks the bucket.
  • Check the observed split with a chi-square test to catch sample ratio mismatch.

Sources

  1. Flag evaluation rules in server-side SDKs — LaunchDarkly documentation, 26 Oct 2026
  2. Percentage rollouts — LaunchDarkly documentation, 26 Oct 2026
  3. How Evaluation Works — Statsig documentation, 26 Oct 2026
  4. Fractional Operation — flagd (OpenFeature project), 26 Oct 2026
  5. Evaluation Context — OpenFeature specification, 26 Oct 2026
  6. Diagnosing Sample Ratio Mismatch in Online Controlled Experiments — ACM KDD 2019 (Fabijan et al.), 1 Jan 2019
  7. Trustworthy Online Controlled Experiments — Cambridge University Press (Kohavi, Tang, Xu), 1 Jan 2020
  8. Assign Experiment Variants at Scale in Online Controlled Experiments (arXiv 2212.08771) — arXiv, 1 Dec 2022
  9. Rust provider fractional bucketing issue — OpenFeature rust-sdk-contrib repository, 26 Oct 2026
  10. Pitfalls of Long-Term Online Controlled Experiments (Kohavi slides) — Microsoft Experimentation Platform (Kohavi), 17 May 2017