A gradual rollout is the practice of releasing a change to a small slice of users first, watching what happens, and only then widening the audience. Instead of one big moment where 100% of traffic hits new code, you climb a ladder: 1%, 10%, 25%, 50%, 100%. At each rung you check your metrics, and at each rung you can stop. Done well, a gradual rollout turns "did we just break production?" from a company-wide panic into a small, contained question with a known answer.
This is the playbook: the ladder, what to watch at each step, hold times, when to pause versus kill, and the mistakes that quietly ruin the exercise.
Before you touch the percentage slider
Three things need to be true before 1% of real users see anything new.
1. Roll out by user, not by request
This is the most common mistake. If you randomize per request, the same user gets the new checkout on one page load and the old one on the next. Their cart looks different every refresh, and your metrics are garbage because every user is in both cohorts at once.
The fix is consistent hashing. Hash a stable identifier (user ID, or a device ID for logged-out traffic) together with the flag key, and map the result onto a 0 to 100 range. A user who lands in bucket 7 is in bucket 7 on every request, on every server, forever. When you raise the rollout from 10% to 25%, the original 10% stay enrolled and 15% join them; nobody falls back out. Any serious feature flags platform does this for you; if you are hand-rolling flags, this is the part not to skip.
2. Decide your guardrail metrics up front
Pick them before the rollout starts, not while staring at a dashboard at 2 a.m. You want three kinds:
- Error rate. HTTP 5xx, unhandled exceptions, or failed jobs in the code path the flag controls. Compare the flagged cohort against the control cohort, not against last week.
- Latency. p95 and p99 for the affected endpoints. New code is often correct but slow, and slow at p99 is invisible at the median.
- A business guardrail. Checkout completion, signup rate, search clicks, whatever the feature is supposed to help or at least not hurt. A release can be error-free and still cost you conversions.
Write down the thresholds too. For example: "kill if cohort error rate exceeds control by 0.5 points, pause if p95 rises more than 15%." Numbers agreed in daylight beat judgment calls made under pressure.
3. Have the exit ready
Know exactly how you turn the feature off, and how long that takes. Flipping a kill switch on a flag takes seconds; reverting a commit and redeploying takes 30 to 45 minutes on most teams. If your only rollback is a redeploy, you do not really have a gradual rollout, you have a slow-motion big bang. We cover this in depth in our kill switches post.
Stage 0: internal users and beta cohorts
Before any percentage of the public, target people who expect rough edges. Start with a rule like "everyone on our team" (match on email domain or a staff attribute), then widen to an opt-in beta list. This stage catches the embarrassing bugs while the blast radius is people who will file a ticket instead of churning. Hold here until internal users have exercised the real flows in production; staging never has real data, real payment methods, or real ad-blockers.
The ladder: 1, 10, 25, 50, 100
Each rung has a job. The percentages are conventions, not laws, but they work because each step roughly doubles or triples exposure, so a problem that was too rare to see at the last rung becomes visible at the next one.
| Stage | What it catches | Minimum hold |
|---|---|---|
| 1% | Crashes, hard errors, "it does not work at all" | 24 hours |
| 10% | Latency shifts, rare-path bugs, early metric drift | 24 to 48 hours |
| 25% | Load-dependent issues, statistically real conversion changes | 48 hours |
| 50% | Capacity, cache hit rates, downstream service pressure | 24 to 48 hours |
| 100% | Nothing left to catch; schedule flag cleanup | n/a |
The 1% soak
The 1% stage feels pointless and is not. Its job is to prove the code executes at all in production: config present, migrations applied, third-party keys valid, nothing throwing on startup. Say your baseline error rate is 0.2%; even a handful of exceptions from the 1% cohort stands out because the cohort is small and the comparison is clean.
Hold for at least 24 hours, and make sure the hold spans a peak traffic period. A rollout raised at 11 p.m. and widened at 8 a.m. has never seen your busiest hour. Weekly patterns matter too: a Saturday soak proves little for a weekday product.
10% and 25%: the metrics stages
At 1%, most business metrics are too noisy to read. By 10% and 25% the cohort is finally big enough for comparisons to mean something, so this is where you watch the conversion guardrail seriously. The mechanics are the same as an experiment: two cohorts, one variable, compare outcomes. If you already run experiments through A/B testing software, a percentage rollout is just an A/B test where you intend B to win. Our A/B testing post covers reading those numbers without fooling yourself.
Latency deserves special attention here. Watch p99, not just the average. A new code path that adds one extra database query per request can look fine at the median and terrible for your heaviest users.
50%: the capacity stage
Half of traffic is where infrastructure problems surface: connection pools sized for the old path, caches the new path bypasses, a downstream service that was fine at 25% and saturates at 50%. If the feature changes how you call other systems, warn their owners before this stage, not after.
100%: finish the job
Going to 100% is not the end. Leave the flag in place for a week or two as a safety net, then delete it: remove the old code path, drop the conditional, close the ticket. A feature toggle that shipped months ago and still guards live code is an untested branch waiting for someone to flip it by accident.
Exit criteria: pause or kill?
Every step ends in one of three ways, and it helps to define them crisply.
- Advance when the hold time has passed, error rate and latency for the cohort match control within your thresholds, and the business guardrail is flat or better.
- Pause when something is off but not on fire: a metric drifting toward its threshold, a spike you cannot yet explain, a dependency team asking for time. Pausing costs nothing. The cohort keeps its consistent experience while you investigate, which is exactly what consistent hashing buys you.
- Kill when a threshold is breached or users are visibly harmed: data written wrong, payments failing, error rate clearly above control. Do not debug at 25% exposure. Flip the flag to 0%, confirm recovery, then debug with the pressure off. Killing a rollout is not a failure; it is the system working.
The bias should be: pause early and often, kill without ceremony. The expensive failure mode is the team that hesitates because turning it off "looks bad."
Automate the ladder
Manual rollouts fail in a boring way: someone raises to 10% on Friday, forgets over the weekend, and the rollout either stalls for a week or gets jumped straight to 100% to catch up. Two pieces of automation fix this:
- Scheduled steps. Define the ladder and hold times once, and let the platform advance automatically when each hold expires.
- Automatic halt on regression. Wire your guardrail metrics to the rollout so a breach pauses or reverts it without waiting for a human to be awake.
In code, the check stays trivially simple either way. The SDK evaluates the flag locally against the user, so the rollout percentage never appears in your code:
const enabled = await toggled.isEnabled('checkout.new-flow', {
userId: user.id,
});
if (enabled) {
return renderNewCheckout();
}
return renderOldCheckout();
Everything else, the percentage, the targeting rules, the schedule, lives in the flag configuration where you can change it in seconds.
Common mistakes
- Rolling out by request instead of by user. Covered above, and worth repeating: it breaks the user experience and the statistics at the same time.
- Skipping the 1% soak. Jumping from internal users to 25% because "it worked on staging" is how staging-only bugs meet a quarter of your customers at once.
- Holding for time instead of traffic. Twenty-four quiet hours can mean almost no cohort traffic. Hold for a minimum number of exposed sessions, not just hours on a clock.
- Watching only errors. A release that quietly drops checkout conversion by a few percent will sail through an error-rate-only rollout.
- Confusing a percentage rollout with a canary. A canary deployment shifts traffic between server versions at the infrastructure layer; a percentage rollout splits users at the application layer. They compose well, but a canary cannot target internal users first or hold a user in a stable cohort.
- Never cleaning up. Every 100% flag left behind makes the next incident harder to reason about.
Start climbing
The ladder is not complicated: internal first, then 1, 10, 25, 50, 100, with defined guardrails, real hold times, and a kill switch you trust. What makes it work is discipline at the boring steps, especially the 1% soak everyone wants to skip.
Toggled is built around exactly this workflow: percentage rollouts with consistent hashing, targeting rules for internal and beta cohorts, a kill switch that takes a release from 100% to 0% in about 3 seconds, and SDKs for React, Next.js, Node.js, Python, Go, and more. You can see the flow end to end on the how it works page. Toggled is currently in early access with flat per-team pricing and unlimited seats, and there is a live demo on the homepage if you want to try a rollout ladder yourself.