FEATUREFLOW V2AI-native · MCP-enabled ·Only 5 spots left at 50% off:NEXT50claim →
Continuous DeliveryJul 24, 2026

How Percentage Rollouts Actually Work: Deterministic Bucketing Explained

J
Jordan Mitchell
Software Engineer

You set a flag to 10% and a user refreshes the page five times. Do they see the new feature once out of ten refreshes?

No — and if your flag system says otherwise, you have a bug, not a rollout. A percentage rollout isn't a dice roll on every request. It's a stable assignment: each user lands in a bucket once, and stays there.

The Problem With Random

Naive implementations call Math.random()at evaluation time. That gets the aggregate percentage right and everything else wrong. Users flip between old and new UI on every page load. Your A/B test data is garbage because nobody had a consistent experience. Support gets "the button keeps disappearing" tickets. And storing assignments in a database to fix it adds a lookup to your hottest code path.

The standard solution needs no storage at all: hash your way to consistency.

Hash, Mod 100, Compare

Featureflow's SDKs compute a bucket by hashing three values together — a salt, the feature key, and the user's bucket key — then taking the result mod 100:

import { createHash } from 'crypto';

/** Stable bucket in [0, 100) for a user + feature pair. */
function bucket(salt: string, featureKey: string, userKey: string): number {
  const hash = createHash('sha1')
    .update(`${salt}:${featureKey}:${userKey}`)
    .digest('hex');
  return parseInt(hash.substring(0, 15), 16) % 100;
}

// A 10% rollout serves the new variant when bucket < 10
const inRollout = bucket(salt, 'new-checkout', user.key) < 10;

Three properties fall out of this, and they're what make percentage rollouts usable in production:

  • Sticky. Same inputs, same bucket — on every request, every server, every SDK. No session state, no assignment table.
  • Expandable. Moving from 10% to 50% widens the threshold. Everyone below 10 is still below 50, so early users keep the feature — nobody gets it taken away mid-rollout.
  • Independent per flag. Because the feature key is part of the hash, a user in the first 10% of one flag isn't automatically in the first 10% of every flag. Without that, the same slice of your user base would absorb the risk of every rollout you run.

Choose Your Bucket Key Deliberately

The one real decision left is what identifier goes into the hash. A user ID keeps the experience consistent across a person's devices. An anonymous session key is fine for logged-out traffic but re-buckets people when the session rotates. An organisation ID rolls out to whole teams at once — usually what you want in B2B, where two colleagues comparing screens should see the same thing.

Whatever you pick, keep it stable. A bucket key that changes on every visit degrades your careful deterministic rollout right back into Math.random().

This is exactly how Featureflow evaluates percentage rollouts across all of its server and client SDKs — the same user gets the same variant whether the flag is evaluated in your Java backend or the browser. See the docs for rollout and targeting details.

#FeatureFlags#PercentageRollouts#ProgressiveDelivery#ContinuousDelivery#DevOps

Roll out to 1%, 10%, 100% — deterministically

Start free with Featureflow and get sticky percentage rollouts out of the box.

Start Now (Free)

Related Articles