Rollouts and Bucketing
How the user token, per-flag seeds and 100,000 buckets make rollouts and variant splits stable, and what changes when you edit a percentage
Copy this guide and your current settings as a prompt for your AI agent.
Preview agent promptSee the guide, code and settings included when you copy.
Percentage rollouts and variant splits need to give the same person the same answer on every request and every Edge location, and must not move people around when you widen a rollout. This page explains how that works and what each kind of edit does to existing users.
On this page
01The user token
Rollouts and splits hash the visitor’s user token. In the browser the SDK
resolves it in this order: init({ userToken }), then the latest
setUserToken(), then the coframe.user.token cookie, then a new random
UUID. The cookie is written only after consent and lives 400 days on the
registrable domain. On the server, only the userToken you pass counts.
Without a token, any flag that needs a rollout or a split answers
enabled: false with reason missing_user_token and sets
errorsWhileComputingFlags: true. Only a 100 % rollout skips bucketing; a 0 %
rollout still needs a token and never matches.
02Two seeds per flag
Every flag gets two random seeds when it is created: one for the rollout, one for the variant split. Editing the flag keeps them, so nobody moves when you change targeting, payloads or percentages. The two seeds differ, so being in the rollout says nothing about which variant you get.
03From token and seed to a slot
The Edge hashes the raw token with the flag’s seed (MurmurHash3, 32-bit) and scales the result to one of 100,000 slots. The same token always lands in the same slot for that flag, and two 10 % flags do not hit the same 10 % of people.
- Rollout: a user gets the flag when their rollout slot is in
0 … slots − 1, with slots = percentage × 1,000. 10 % is slots 0 to 9,999; 12.5 % is 0 to 12,499. - Variants: the split is cut into ranges one after the other, in the order
the variants are listed. At 50 / 50,
controlis 0 to 49,999 andcompactis 50,000 to 99,999.
04When a percentage changes
The seeds stay the same, so every user keeps their slot. Only the ranges move. Measured on 100,000 sample tokens:
| Change | New range | What happens to users | Measured |
|---|---|---|---|
| Rollout 10 % → 20 % | 0–9,999 → 0–19,999 | Everyone who had it keeps it; 10 % more get it | 0 left |
| Rollout 20 % → 10 % | 0–19,999 → 0–9,999 | Users in 10,000–19,999 lose it | 10,066 lost it |
| Split 50 / 50 → 70 / 30 | control 0–69,999, compact 70,000–99,999 |
Users in 50,000–69,999 move from compact to control |
19,977 moved |
| Add a third variant (34 / 33 / 33) | 0–33,999 / 34,000–66,999 / 67,000–99,999 | About half of all users change variant | 48,900 moved |
| Reorder the variants | Ranges follow the new order | Users can switch variant | — |
| Edit targeting or payloads | Unchanged | Nobody is re-bucketed | — |
05How one flag is decided
Condition sets are checked in order, in memory on the Edge, with no network calls. For a toggle any passing set means on; for multiple variants the first matching set wins.
| Step | Result |
|---|---|
| Flag switched off in the dashboard | enabled: false, reason inactive |
| Remote config | enabled: true with the payload, reason remote_config; no condition consulted, no token needed |
| Toggle: a condition set matches and the user is inside its rollout | enabled: true (+ payload), reason condition_match |
| Variants: the first matching set forces a variant or splits by the allocation | enabled: true, variant, that variant’s payload |
| Nothing matches | enabled: false, reason no_matching_condition |
| A rollout or split is needed but there is no user token | enabled: false, reason missing_user_token, errorsWhileComputingFlags: true |
Every miss returns enabled: false and a reason, never null.
# Rollouts and Bucketing
How the user token, per-flag seeds and 100,000 buckets make rollouts and variant splits stable, and what changes when you edit a percentage
Percentage rollouts and variant splits need to give the same person the same
answer on every request and every Edge location, and must not move people
around when you widen a rollout. This page explains how that works and what
each kind of edit does to existing users.
## The user token
Rollouts and splits hash the visitor’s user token. In the browser the SDK
resolves it in this order: `init({ userToken })`, then the latest
`setUserToken()`, then the `coframe.user.token` cookie, then a new random
UUID. The cookie is written only after consent and lives 400 days on the
registrable domain. On the server, only the `userToken` you pass counts.
Without a token, any flag that needs a rollout or a split answers
`enabled: false` with reason `missing_user_token` and sets
`errorsWhileComputingFlags: true`. Only a 100 % rollout skips bucketing; a 0 %
rollout still needs a token and never matches.
## Two seeds per flag
Every flag gets two random seeds when it is created: one for the rollout, one
for the variant split. Editing the flag keeps them, so nobody moves when you
change targeting, payloads or percentages. The two seeds differ, so being in
the rollout says nothing about which variant you get.
## From token and seed to a slot
The Edge hashes the raw token with the flag’s seed (MurmurHash3, 32-bit) and
scales the result to one of 100,000 slots. The same token always lands in the
same slot for that flag, and two 10 % flags do not hit the same 10 % of
people.
- Rollout: a user gets the flag when their rollout slot is in
`0 … slots − 1`, with slots = percentage × 1,000. 10 % is slots 0 to 9,999;
12.5 % is 0 to 12,499.
- Variants: the split is cut into ranges one after the other, in the order
the variants are listed. At 50 / 50, `control` is 0 to 49,999 and `compact`
is 50,000 to 99,999.
## When a percentage changes
The seeds stay the same, so every user keeps their slot. Only the ranges move.
Measured on 100,000 sample tokens:
Change |
New range |
What happens to users |
Measured |
Rollout 10 % → 20 % |
0–9,999 → 0–19,999 |
Everyone who had it keeps it; 10 % more get it |
0 left |
Rollout 20 % → 10 % |
0–19,999 → 0–9,999 |
Users in 10,000–19,999 lose it |
10,066 lost it |
Split 50 / 50 → 70 / 30 |
`control` 0–69,999, `compact` 70,000–99,999 |
Users in 50,000–69,999 move from `compact` to `control` |
19,977 moved |
Add a third variant (34 / 33 / 33) |
0–33,999 / 34,000–66,999 / 67,000–99,999 |
About half of all users change variant |
48,900 moved |
Reorder the variants |
Ranges follow the new order |
Users can switch variant |
— |
Edit targeting or payloads |
Unchanged |
Nobody is re-bucketed |
— |
Rollout and split are independent
With a 10 % rollout and a 50 / 50 split, about 5 % of all users see
`compact`. To keep people in their variant, change the rollout percentage,
not the split, and do not reorder or add variants while a test runs.
## How one flag is decided
Condition sets are checked in order, in memory on the Edge, with no network
calls. For a toggle any passing set means on; for multiple variants the first
matching set wins.
Step |
Result |
Flag switched off in the dashboard |
`enabled: false`, reason `inactive` |
Remote config |
`enabled: true` with the payload, reason `remote_config`; no condition consulted, no token needed |
Toggle: a condition set matches and the user is inside its rollout |
`enabled: true` (+ payload), reason `condition_match` |
Variants: the first matching set forces a variant or splits by the allocation |
`enabled: true`, `variant`, that variant’s payload |
Nothing matches |
`enabled: false`, reason `no_matching_condition` |
A rollout or split is needed but there is no user token |
`enabled: false`, reason `missing_user_token`, `errorsWhileComputingFlags: true` |
Every miss returns `enabled: false` and a reason, never `null`.