How Would You Design a Feature Flag System?
Learn how to design a feature flag system: flag stores, in-process SDK evaluation, deterministic rollouts, and safe fail-open defaults.
Expected Interview Answer
A feature flag system decouples code deployment from feature release by storing flag state in a fast, centrally managed store that services poll or subscribe to, so a flag can be flipped on or off at runtime without shipping new code.
The core pieces are a flag definition store (name, type, default value, targeting rules), an evaluation SDK embedded in each service that resolves a flag for a given user or request context using local cached rules, and a streaming or polling update mechanism so changes propagate within seconds rather than requiring a redeploy. Targeting rules typically support percentage rollouts, user or attribute-based segments, and environment overrides, evaluated deterministically via a hash of a stable ID so the same user always gets the same variant. For reliability, SDKs cache the last-known rule set locally and fail open to a safe default if the flag service is unreachable, since a flagging outage must never take down the whole product. At scale, evaluation must happen in-process (not a network call per flag check) to keep request latency near zero.
- Decouples deployment from release, enabling safe canary and percentage rollouts
- Allows instant kill-switches for buggy features without a redeploy
- Supports targeted rollouts by user segment, region, or plan tier
- Keeps evaluation latency near zero via local, in-process rule evaluation
AI Mentor Explanation
A feature flag system is like a captain deciding mid-match, without changing the squad, whether to switch on an attacking field or a defensive one based on the situation. The tactic (feature) already exists in the playbook, but a signal from the dugout (the flag store) tells each fielder in real time which version to run. If the tactic backfires, the captain reverts the signal instantly rather than waiting for a new team sheet next match. That instant, code-unchanged behavior switch is exactly what a feature flag provides.
Step-by-Step Explanation
Step 1
Define the flag
Create a flag with a name, type (boolean/string/percentage), default value, and targeting rules in the central flag store.
Step 2
Embed the SDK
Services embed an evaluation SDK that caches the rule set locally and streams or polls updates from the flag service.
Step 3
Evaluate deterministically
On each request, the SDK hashes a stable ID (user, account) against the rules to resolve a consistent variant with no network call.
Step 4
Roll out and monitor
Gradually widen the percentage rollout while monitoring metrics, with a one-click kill switch to revert instantly if issues appear.
What Interviewer Expects
- Separates flag definition/storage from in-process evaluation for latency reasons
- Explains deterministic hashing so the same user consistently gets the same variant
- Mentions fail-open/safe-default behavior when the flag service is unreachable
- Discusses percentage rollouts, targeting rules, and instant kill switches
Common Mistakes
- Making a network call per flag evaluation instead of caching rules locally
- Forgetting a safe default when the flag service is down (should fail open, not crash)
- Not making rollout deterministic per user, causing flapping between variants
- Treating flags as permanent instead of cleaning up stale flags after full rollout
Best Answer (HR Friendly)
โA feature flag system lets us turn features on or off for specific users instantly, without deploying new code. We ship the code disabled behind a flag, gradually turn it on for more people while watching for issues, and can hit a kill switch immediately if something goes wrong.โ
Code Example
function evaluateFlag(flag, userId) {
if (!flag.enabled) return flag.defaultValue
for (const rule of flag.targetingRules) {
if (rule.type === "segment" && userInSegment(userId, rule.segment)) {
return rule.value
}
}
// deterministic percentage rollout via stable hash
const bucket = hashToPercent(`${flag.key}:${userId}`) // 0-99
return bucket < flag.rolloutPercent ? flag.enabledValue : flag.defaultValue
}
function hashToPercent(input) {
let h = 0
for (let i = 0; i < input.length; i++) {
h = (h * 31 + input.charCodeAt(i)) >>> 0
}
return h % 100
}Follow-up Questions
- How would you keep flag evaluation fast when a service handles millions of requests per second?
- How do you avoid an unbounded pile-up of stale flags in a large codebase?
- What happens to a user mid-session if a percentage rollout changes their bucket assignment?
- How would you audit who changed a flag and roll back a bad change quickly?
MCQ Practice
1. Why should flag evaluation happen in-process rather than via a network call per check?
Evaluating flags from a locally cached rule set avoids adding network latency and a dependency on every request.
2. Why is deterministic hashing used for percentage rollouts?
Hashing a stable ID ensures consistent bucket assignment, avoiding a jarring flip-flop experience for the same user.
3. What should an SDK do if it cannot reach the flag service?
SDKs should fail open using the last-cached rules or a safe default so a flag service outage does not take down the product.
Flash Cards
What is a feature flag system? โ A mechanism to toggle features at runtime via centrally managed flags, decoupling deploy from release.
Why cache rules locally in the SDK? โ To evaluate flags in-process with near-zero latency instead of a network call per check.
Why hash a stable user ID for rollout? โ To deterministically and consistently bucket the same user into the same variant.
Fail-open behavior? โ When the flag service is unreachable, the SDK falls back to a cached or safe default instead of failing the request.