Percentage rollouts and sticky bucketing
A percentage rollout turns a feature on for a share of users, chosen by a stable hash so each user keeps the same answer as you widen it. Flagger.gg buckets each user by the first 4 bytes of sha256(key:userId) mod 100, so going from 10% to 25% keeps the first 10%. Anonymous users stay off until the flag is fully on.
Updated
Roll out step by step
- Start with admins.
flagger set new_search admins --note "team first"turns it on for your admins only. - Go to a small percentage.
flagger set new_search percent --percent 10 --note "admins fine". Admins stay on. - Check who's in.
flagger eval new_search --user usr_123says whether one user is on, and why. - Widen it. Raise the percentage in steps (25, 50) as your checks stay healthy. Every user already in stays in.
- Turn it on for everyone, then remove the flag.
flagger set new_search on, and once it's stable, delete the flag and the old code path.
How sticky bucketing works
For a flag in percent, Flagger.gg computes a bucket from 0 to 99 for each user:
bucket = first 4 bytes of sha256(key + ":" + userId), read big-endian, mod 100
on = bucket < percent
For example, for the key new_search, usr_456 lands in bucket 22 and usr_123 in bucket 48. At 10% neither is in; at 25% usr_456 is; at 50% both are. Because a user's bucket never changes for a flag, raising the percentage only ever adds people, and lowering it removes the most recently added first.
The key is part of the hash, so the same user lands in different buckets for different flags: your first 10% isn't the same 10% on every rollout. Both SDKs and the service run the same shared test vectors, so Go, Node and the service's eval always agree.
In code
The check is the same as for any flag; the SDK does the bucketing locally.
// Go
on := client.Enabled("new_search", &flagger.Subject{UserID: user.ID, Email: user.VerifiedEmail})
b := flagger.Bucket("new_search", user.ID) // 0-99, for logs or debugging
// Node / TypeScript
const on = flagger.flag("new_search", { userId: user.id, email: user.verifiedEmail });
const { reason } = flagger.explain("new_search", { userId: user.id }); // e.g. "bucket 37 ≥ 10%"
Rules worth knowing
- Anonymous users are off for every state but
on: with no user id there's nothing stable to hash. - Admins are always in for
admins,targetsandpercent. - Rules are kept. Switching from
percenttoadminsand back keeps the percentage and targets. - Agents and the cap. With approvals on, an agent in production can set
percentup to the cap (10% by default) on its own; above that it files a request a person approves. Lowering a percentage is always allowed.
In development Scheduled rollouts (1 → 5 → 25 → 100% on a timer) and guarded rollouts that watch metrics and roll back on their own are in development.
FAQ
How do I do a percentage rollout with feature flags?
Put the feature behind a flag, set it to a percentage such as 10%, check your metrics, then raise it in steps to 100%. With Flagger.gg: flagger set <key> percent --percent 10.
What is sticky bucketing?
Assigning each user to a fixed bucket with a stable hash of the flag key and user id, so the same user always gets the same answer at the same percentage, and stays in as the percentage rises.
Does a percentage rollout work for logged-out visitors?
Not in Flagger.gg: callers with no user id see only flags that are fully on.
How do I do a percentage rollout with the Go SDK?
Set the flag to percent and call client.Enabled(key, &flagger.Subject{UserID: id}). The Go SDK buckets locally with flagger.Bucket(key, userID), the same function the service uses.
Ship your next change behind a flag.
Sign up, add an SDK key to your app and give your agent a token.