# Percentage rollouts

> Deterministic, cross-language rollout bucketing.

A percentage rollout enables a flag for a deterministic fraction of your users —
10% first, then 25%, then everyone — without code changes. It's how you ramp a
feature safely.

## Setting a rollout

Each flag-environment has a `rollout_pct`. Think of it as the **fallthrough**:
when the flag is enabled and a user matches no targeting rule, the rollout decides
whether they get the enabled value.

- `rollout_pct = 25` → roughly 25% of users get the flag.
- `rollout_pct = 0` → nobody gets it via the rollout (rule matches still do).
- `rollout_pct = 100` → **everyone** gets it.
- **unset** (no rollout configured) → the default for a new flag — see the note
  below for how it resolves.

## Deterministic bucketing

A user is bucketed by hashing their `user_id` together with the flag key:

```text
bucket = sha256("{user_id}:{flag_key}") % 100
user is in the rollout if  bucket < rollout_pct
```

Two consequences fall out of this:

- **Stable per user.** The same user always lands in the same bucket for a given
  flag, so they don't flicker between on and off across requests or page loads.
  User "42" either always sees the feature at 25% or never does.
- **Stable as you ramp.** Because the bucket doesn't depend on the percentage,
  everyone in the 10% cohort is still included when you go to 25% — you only ever
  *add* users as you increase the percentage, never reshuffle them.

A `user_id` (or `id`) is **required** for rollouts. With no user context, a flag at
less than 100% can't bucket the caller, so it falls back to the default value.

## Identical across languages

The hashing is part of the cross-language SDK contract: Python and JavaScript
compute the **same** bucket for the same `user_id:flag_key`. A user bucketed into
the 25% cohort gets the feature whether your backend (Python) or your frontend
(JavaScript) does the check. This parity is pinned by a shared set of test vectors
that both SDKs run.

## The "no rollout configured" default

A new flag-environment starts with **no rollout configured**, and what that means
depends on whether you've added targeting rules:

- **No rules, no rollout** → the flag is a plain on/off switch: enabled serves
  everyone, disabled serves no one.
- **Rules, no rollout** → only the users who **match a rule** get the flag; the
  fallthrough is effectively 0%. Adding a rule narrows the audience to exactly
  that rule — it doesn't leak to everyone else.
- **Rules + a rollout** → rule matchers always get it, **plus** the rollout
  percentage of everyone else.

Internally "no rollout configured" is stored as a null `rollout_pct`. With no
rules it resolves to 100 (everyone); with rules present the backend publishes it
as a 0% fallthrough so the rules actually scope the audience. The dashboard shows
the rollout slider once you deliberately set a percentage — and you **can** set it
to a full **100%** (an explicit "serve everyone," distinct from leaving it unset).
(See ADR-008 for the reasoning and the resolution rule — the SDKs read the
resolved number, so the evaluator itself stays a simple `bucket < rollout_pct`
check.)

## Next

- [Evaluation order](/docs/reference/evaluation-order) — how rules and rollout
  interact, step by step.
- [Targeting rules](/docs/concepts/targeting) — enable for specific users
  regardless of the rollout.
