Feature flags on the server

Cloud, deploys and infrastructure Mid-level Python 3.13+ PostgreSQL 17+ Reviewed By Best Latest Apps

Predict the output

Is the 10% group inside the 25% group, and close to 1,000 users?

Python 3.13
import hashlib


def in_rollout(user_id: str, flag: str, percent: int) -> bool:
    h = hashlib.sha256(f"{flag}:{user_id}".encode()).digest()
    bucket = int.from_bytes(h[:4], "big") % 100
    return bucket < percent


users = [f"u{i}" for i in range(10_000)]
on_10 = {u for u in users if in_rollout(u, "new-checkout", 10)}
on_25 = {u for u in users if in_rollout(u, "new-checkout", 25)}
print(on_10 <= on_25, 900 < len(on_10) < 1100)

Options
Hint 1 of 1

If bucket < 10, is bucket < 25?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A True True

Correct. Buckets are stable, so raising the percent only adds users.

Hashing the flag and user id gives each user a fixed bucket from 0 to 99. Going from 10% to 25% only adds buckets 10 to 24, and a good hash puts about 1,000 of 10,000 users below 10.

Why not the others?
  • B False True. A user in bucket < 10 is also < 25.
  • C True False. SHA-256 spreads 10,000 users evenly enough.
  • D Different every run. The hash is deterministic.

30-second answer

A flag lets you deploy code dark and turn features on per user, percentage or tenant without a deploy. Evaluate flags server-side from a fast local cache, use a stable hash of user and flag for percentage rollouts so each user's experience doesn't flicker, and give every flag an owner and an expiry. Delete the flag and the dead branch once it's fully rolled out; old flags become hidden complexity.

Practise saying it · 25 s. No recording, just a timer

Feature flags.

Say your answer out loud, then check which of these points you covered:

  • Deploy ≠ release
  • Stable hash bucketing
  • Local cache
  • Owners and expiry
  • Kill switches

Deep dive

Kill switches for risky dependencies are long-lived flags on purpose.

Hashing flag + user means different flags pick different users.

Log flag evaluations with requests for debugging.

Test both branches while the flag exists.

Stable percentage rollout Ran on Python 3.13.5 · mypy --strict
Python 3.13
import hashlib


def in_rollout(user_id: str, flag: str, percent: int) -> bool:
    h = hashlib.sha256(f"{flag}:{user_id}".encode()).digest()
    bucket = int.from_bytes(h[:4], "big") % 100
    return bucket < percent


users = [f"u{i}" for i in range(10_000)]
on_10 = {u for u in users if in_rollout(u, "new-checkout", 10)}
on_25 = {u for u in users if in_rollout(u, "new-checkout", 25)}
print(on_10 <= on_25, 900 < len(on_10) < 1100)

Common mistakes

  • Random per request, so users flip between versions.
  • Hundreds of stale flags.
  • Calling the flag service over the network on every check.

Senior take & follow-ups

Deploy vs release, stable bucketing, local evaluation, owners and cleanup.

Follow-up questions

Think of your answer first, then open the hint.

  1. Why hash flag and user together?

    Show hint

    So the same users aren't always first into every rollout.

  2. What's a kill switch?

    Show hint

    A permanent flag to disable a feature or dependency fast.

Similar on Android: Feature flags and staged rollouts The Android page reads flags in the app; here the server owns the rollout and bucketing.

Suggest a fix if something here is wrong or out of date.