# Feature Flags, Percent Rollouts and Kill Switches — Safe Rollout Plans for AI Features

Source: https://www.skillbyai.com/en/ai-feature-rollouts/f-flags

> Stable bucketing and an off button.

## Deterministic buckets

A **feature flag** decides at runtime whether a user gets the AI feature. Percent rollouts should be **deterministic**: hash the user id with the feature name into a bucket from 0 to 99 and enable the feature if the bucket is below the rollout percentage. Users then keep a consistent experience, and raising the percentage only adds users. A **kill switch** turns the feature off for everyone instantly, falling back to the non-AI path. Flag services (LaunchDarkly, Unleash, open-source flagd, cloud config services) provide this with audit logs.

## Control exposure without deploying

Flags let you choose who sees the feature, change it instantly and switch it off in seconds.

![Three ideas: flags and kill switches, cohorts, config as data.](assets/figures/ai-feature-rollouts/section-3-map.svg) — Figure 3.1 — Flags, cohorts and config.

## Hash-based percent rollout and kill switch, run

I ran this with Python 3 (scipy 1.18.1 where imported) on example numbers, not data from a real product. Across 10,000 users, rollouts of 1%, 10% and 50% enable 0.9%, 10.1% and 50.0%. Everyone enabled at 10% is still enabled at 50%, and turning on the kill switch enables nobody.

```python
import hashlib
def bucket(user_id, feature):          # stable 0-99 bucket per user and feature
    h = hashlib.sha256(f"{feature}:{user_id}".encode()).hexdigest()
    return int(h[:8], 16) % 100
def enabled(user_id, feature, percent, kill_switch=False):
    return (not kill_switch) and bucket(user_id, feature) < percent
users = [f"user-{i}" for i in range(10000)]
for pct in [1, 10, 50]:
    share = sum(enabled(u, "ai-summary", pct) for u in users) / len(users)
    print(f"rollout {pct:>2}% -> {share:.1%} of 10,000 users enabled")
on_at_10 = {u for u in users if enabled(u, "ai-summary", 10)}
on_at_50 = {u for u in users if enabled(u, "ai-summary", 50)}
print("everyone enabled at 10% stays enabled at 50%:", on_at_10 <= on_at_50)
print("kill switch on ->", sum(enabled(u, "ai-summary", 50, kill_switch=True) for u in users), "users enabled")
```

Output:

```
rollout  1% -> 0.9% of 10,000 users enabled
rollout 10% -> 10.1% of 10,000 users enabled
rollout 50% -> 50.0% of 10,000 users enabled
everyone enabled at 10% stays enabled at 50%: True
kill switch on -> 0 users enabled
```

## Test the kill switch before launch

Flip it in staging and in production during dogfooding; an untested off switch is not a safety mechanism.

**Quiz:** Why hash the user id for percent rollouts instead of choosing randomly per request?

- [x] Each user gets a consistent experience and raising the percentage only adds users
- [ ] Hashing makes the model faster
- [ ] Random choice is illegal
- [ ] Hashing reduces token cost

*Answer:* Each user gets a consistent experience and raising the percentage only adds users. Stable buckets avoid flip-flopping.
