Bell Statistics

What is bucketing?

Bucketing is the process of deciding which variant each unit receives. It has to be random, deterministic for a given unit, and independent across concurrent experiments — and failures in any of those three show up as results that look fine and are not.

Also called
assignment, unit assignment, bucket, variant assignment, allocation
Allon Korem

Written by Allon Korem

Chief Executive Officer

Last updated

In plain English

Bucketing is the mechanism that puts each unit into a variant. It sounds like plumbing and it is the foundation everything else rests on: if assignment is not what it claims to be, every downstream statistic describes something other than the experiment you thought you ran. Three properties are required, and each fails in a characteristic way.

It must be random with respect to everything that predicts the outcome. That is what makes the arms comparable and is the entire basis for reading a difference as an effect. Assignment by user id parity, sign-up order, or anything else with structure will correlate with tenure or behaviour and produce a confounded comparison that looks like a clean one.

It must be deterministic for a given unit. The same user must land in the same variant on every request, every session and every device, or their experience flickers and their data belongs to both arms. This is why hash-based assignment is the standard implementation — it computes the bucket from a stable identifier rather than storing a decision, so it is consistent without needing a lookup and works identically across services.

And it must be independent across concurrent experiments. This is the property most often broken, by hashing only the user id rather than the user id combined with the experiment id. When that happens, the same users land in the same relative position in every experiment: a user in the top decile of the hash space is in variant B of everything. Assignments become correlated across tests, so interaction effects stop being random noise and become systematic, and two experiments running together can be measuring each other.

The verification is straightforward and belongs in the platform rather than in anyone's memory. A sample ratio mismatch check on every experiment catches uneven splits and mid-flight allocation changes; a periodic A/A test catches the subtler failures in metric computation and variance. Both are cheap, both run automatically, and between them they cover most of what can go wrong here.

The formula

The standard implementation is one line, and the detail that matters most is what goes into the hash.

The assignment function
bucket = hash( unit_id + experiment_id ) mod 100

Deterministic, uniform, and independent per experiment. The experiment id is what delivers the third property.

What omitting the experiment id does
same user → same bucket in every experiment

Assignments correlate across concurrent tests, making interactions systematic rather than averaging out.

Mapping bucket to variant
variant = first v where cumulative allocation > bucket

Changing allocations mid-test moves these boundaries and reassigns users — see traffic allocation.

The verification
chi-square goodness of fit against the intended split

Run on every experiment, at a strict threshold — see the chi-square calculator.

Worked example

A platform hashes only the user id, not the user id plus experiment id. Three experiments run concurrently over the same 200,000 users. Each reports a clean sample ratio and each looks healthy in isolation. An analyst checks how assignments relate across the three.

Experiments running
3, all 50/50
SRM check, each experiment
passes (p > 0.4)
Users in variant of all three
99,847 of 200,000 (49.9%)
Expected if independent
25,000 (12.5%)
Correlation between assignments
1.00
Users in a mixed combination
0

Every user is in the same relative position in all three experiments. There are only two distinct populations, not eight.

Each experiment passes its own health check because each split is genuinely 50/50 — the failure is invisible from inside any single test. What has actually happened is that the three experiments are running on two groups of users rather than eight, so no combination of treatments is ever observed except all-control and all-treatment. If any two of the three interact, that interaction is baked into all three results and attributed to whichever change the reader is looking at. The fix is one token in the hash input. The reason this is worth a worked example is that it produces no error, no imbalance and no alert: the only way to find it is to check the cross-tabulation between concurrent experiments, which almost nobody does unless they know to look. Adding a periodic assignment-independence check alongside the SRM check is cheap insurance against a class of failure that otherwise persists indefinitely.

Common misconceptions

Any hash function will do as long as the split comes out even.
An even split is necessary and not sufficient. The hash must also distribute independently of user characteristics — some fast hashes have poor avalanche behaviour and correlate with structured inputs like sequential ids. MurmurHash and similar non-cryptographic hashes designed for uniformity are the standard choice for good reason.
Storing each user's assignment in a database is safer than computing it.
It introduces a lookup on every request, a consistency problem across services, and a migration whenever allocations change. Hashing gives determinism for free and identically everywhere, which is why it is standard. Storage is occasionally necessary — when assignment must survive a change to the hash input — and it is the exception rather than the safer default.
If each experiment shows a balanced split, bucketing is working.
Balance within each experiment says nothing about independence across them. Hashing without the experiment id produces perfectly balanced individual splits and perfectly correlated assignments, so concurrent tests silently share populations. Checking the cross-tabulation between experiments is what catches it.

Frequently asked questions

Why does the experiment id need to be in the hash?
To make assignments independent across concurrent experiments. Hashing the user id alone puts every user in the same relative position in every test, so someone in variant B of one is in variant B of all of them. Interactions between experiments then become systematic rather than averaging out, and two tests running together can be partly measuring each other. Adding the experiment id costs nothing and removes the whole problem.
How do I bucket users who are not logged in?
On a stable client-side identifier such as a first-party cookie, accepting that it is per-device and lost when cookies are cleared. The consequence is some users appearing as several units and some switching arms after clearing storage, which adds noise. Where a user logs in mid-session, reconciling the anonymous and authenticated identifiers is worth doing carefully — inconsistent reconciliation is a common source of sample ratio mismatch.
What happens to bucketing when I change the traffic allocation?
The variant boundaries move, so some users switch arms while carrying their previous behaviour with them. That breaks the comparison and usually shows up as a sample ratio mismatch. If allocation must change, restart the experiment. Doing the risk-management rollout as a separate phase that finishes before the experiment begins avoids the situation entirely.

Related terms

  • Experimentation platform

    Assignment, exposure, analysis and the guardrails — and the last of those is what separates a platform from a flag service.

  • Hash-based assignment

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

  • Randomization

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

  • Traffic allocation

    50/50 is not caution, it is the optimum — and a 90/10 split needs nearly three times the traffic.

  • Concurrent testing

    Overlapping tests are safe for each result and blind to the combination — which is where the surprise lands.

  • Randomization unit

    What gets assigned decides what counts as independent — and that decides whether your p-values mean anything.

Calculate it

  • Chi-square test

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

  • A/B test sample size

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

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