Compile Ready
All low level design problems
Low Level Design/Advanced/Expert Systems

Design a Feature Flag Service

Flags, targeting rules, rollout percentages, and evaluation — a rules-engine + strategy design.

Advanced 50m interview 13m read Medium frequency Popularity 77
Strategy Chain of Responsibility Observer Singleton Atlassian Amazon LinkedIn

Problem Statement

Design a Feature Flag Service similar to LaunchDarkly. Product teams define flags with named variations, ordered targeting rules, user segments, and optional percentage rollouts. Application code asks the service to evaluate a flag for a given UserContext and receives the resolved variation.

The core LLD challenge is the evaluation engine: it must apply rules in a predictable order while staying closed for modification. Adding a rule such as country targeting, segment targeting, plan targeting, or time-window targeting should mean adding a new rule class, not editing a long evaluator switch statement.

Business context

Feature flags decouple deployment from release. Teams can ship dormant code, open it to beta users, roll it out to 5% of traffic, kill it quickly, or run an experiment without a redeploy.

Interviewers use this problem to test whether you can design a small rules engine: deterministic rollout, ordered precedence, stable user bucketing, extensible rule strategies, and change notifications for SDKs or services that cache flag definitions.

Functional Requirements

  • Create or update a flag with a key, enabled state, allowed variations, and a default variation.

  • Evaluate a flag for a user context and return exactly one variation or a caller-supplied fallback when the flag is missing.

  • Support ordered targeting rules; the first matching rule wins.

  • Support user segments such as beta-testers, employees, or enterprise-customers.

  • Support percentage rollouts that deterministically bucket the same user into the same variation for a given flag.

  • Allow new targeting rule types to be added without changing the evaluation engine.

  • Notify listeners when a flag is inserted or updated so SDK caches can refresh.

Non-Functional Requirements

Deterministic evaluation

The same flag key and user key must resolve to the same rollout bucket until the rollout weights change. Random per-request assignment breaks user experience and experiments.

Open for extension

Rule behavior belongs behind TargetingRule. The engine walks an ordered chain and does not know whether a rule checks a segment, attribute, plan, or device.

Low latency

Evaluation should be in-memory and proportional to the number of rules on one flag, not to all flags in the system.

Thread safety

Application threads may evaluate while an admin thread updates a flag. The service uses concurrent collections and immutable flag snapshots.

Operational visibility

Production systems need metrics for evaluated flags, missing flags, rule matches, rollout distribution, and stale SDK caches.

Requirement Clarification

QAre variations only booleans?

No. Model variations as strings in the base design so boolean flags, experiment variants, and configuration choices all fit. A production version can wrap them in a typed value object.

QWhere does segment membership come from?

For LLD, segment membership is already present on UserContext. A production service may hydrate it from an identity service or segment store before evaluation.

QWhat happens when a flag is disabled?

The evaluator immediately returns the flag's default variation. Disabled means no targeting rules or rollouts are applied.

QHow should percentage rollout be computed?

Hash flagKey:userKey into a stable bucket from 1 to 100, then compare that bucket with cumulative variation weights.

QDo we need REST APIs, authentication, or persistence?

Not in the core model. The design focuses on object responsibilities. Persistence, admin APIs, RBAC, and audit logs are production layers around the same domain model.

UML Class Diagram

Rendering diagram…
The flag is an immutable definition; the evaluator is the stable engine; concrete targeting rules are pluggable strategies in an ordered chain; the singleton service owns storage and observer notifications.

Sequence Diagram

Rendering diagram…
Evaluation has three precedence levels: disabled flag default, first matching targeting rule, then deterministic percentage rollout, and finally the default variation.

Entity Identification

FeatureFlag

Immutable flag definition. Holds the key, enabled state, allowed variations, default variation, ordered targeting rules, and fallback rollout.

keyenabledvariationsdefaultVariationrulesrollout

TargetingRule

Strategy interface for a single targeting decision. A rule either returns a variation or returns empty so the next rule can try.

evaluate(context, flag)describe()

PercentageRollout

Deterministic bucketing policy. It maps flagKey:userKey to bucket 1..100 and selects the variation whose cumulative weight covers that bucket.

weightsaddevaluatebucket

UserContext

Immutable snapshot of the caller being evaluated: stable user key, custom attributes, and segment membership.

userKeyattributessegments

FlagEvaluator

Stable evaluation engine. It owns precedence and chain traversal but delegates all rule-specific logic to TargetingRule implementations.

evaluate(flag, context)

FeatureFlagService

Singleton facade over flag storage, evaluation, and update notifications. Application code talks to this boundary rather than the raw map.

flagsevaluatorlistenersgetInstance

FlagChangeListener

Observer contract used by SDK caches, audit sinks, or streaming layers that react to flag changes.

onFlagChanged(flag)

Design Patterns Used

Strategy

Each TargetingRule is a strategy for one kind of match: segment, attribute, plan, region, time window, or anything added later. The evaluator depends only on the interface.

Chain of Responsibility

A flag owns an ordered chain of targeting rules. The evaluator asks each rule in order and stops at the first returned variation, preserving explicit business priority.

Observer

FeatureFlagService notifies FlagChangeListener subscribers after a flag is upserted. SDK caches and audit streams react without being hard-coded into the service.

Singleton

The in-memory demo uses one FeatureFlagService instance so all callers read the same flag registry. In production, the same boundary could wrap a distributed store.

Step-by-Step Design

  1. 1Separate flag definition from flag evaluation

    A FeatureFlag is just data: allowed variations, default, rules, and rollout. It does not decide precedence. This keeps mutation, validation, and evaluation responsibilities clean.

    public final class FeatureFlag {
        private final List<TargetingRule> rules;
        private final PercentageRollout rollout;
        public List<TargetingRule> getRules() { return rules; }
        public PercentageRollout getRollout() { return rollout; }
    }
  2. 2Make every rule a strategy

    The evaluator should never know rule internals. A new rule type implements TargetingRule and returns an Optional variation. Empty means the chain should continue.

    public interface TargetingRule {
        Optional<String> evaluate(UserContext context, FeatureFlag flag);
        String describe();
    }
  3. 3Evaluate rules as an ordered chain

    Business teams expect rule order to matter. Put precise allowlists first, broader attribute rules later, and percentage rollout last.

    for (TargetingRule rule : flag.getRules()) {
        Optional<String> variation = rule.evaluate(context, flag);
        if (variation.isPresent()) {
            return variation.get();
        }
    }
  4. 4Use deterministic percentage rollout

    Hash flagKey:userKey into a bucket from 1 to 100. As rollout moves from 10% to 25%, users already in the first 10 buckets remain enabled and only new buckets are added.

    private int bucket(String flagKey, String userKey) {
        CRC32 crc = new CRC32();
        crc.update((flagKey + ":" + userKey).getBytes(StandardCharsets.UTF_8));
        return (int) (crc.getValue() % 100) + 1;
    }
  5. 5Expose a singleton service boundary

    The service hides the registry map, validates the missing-flag fallback path, and emits update events. Application code should call evaluate, not manipulate flags directly.

  6. 6Notify observers after state changes

    Listeners are called after the new flag snapshot is stored. That ordering prevents subscribers from reading stale state when they refresh their local caches.

Complete Java Implementation

Loading…

Explanation of Every Class

FeatureFlag

Immutable flag snapshot containing all data needed for evaluation: key, enabled state, allowed variations, default, ordered rules, and rollout. It validates the default variation up front.

TargetingRule

Strategy interface plus example rule classes. AttributeEqualsRule checks context attributes, while SegmentRule checks segment membership. Both return empty when they do not match.

PercentageRollout

Deterministic rollout strategy. It uses CRC32 over flagKey:userKey, maps the result to bucket 1..100, and applies cumulative weights.

UserContext

Immutable evaluation input. The fluent methods return new contexts, preventing one request from mutating the attributes or segments used by another request.

FlagEvaluator

Stable engine that enforces precedence: disabled default, ordered targeting chain, percentage rollout, then default. It never switches on rule type.

FeatureFlagService

Singleton facade with an in-memory concurrent registry, evaluation entry point, and observer notification hook for flag changes.

Main

Small demo that registers a flag, adds a listener, defines segment and attribute rules, and evaluates three different user contexts.

Dry Run

Sample input

Flag new-checkout has variations on/off, default off, rules SegmentRule(beta-testers -> on) then AttributeEqualsRule(country=IN -> on), and rollout 25% on / 75% off.

StepContextRule chain resultRollout resultReturned variation
1maya in beta-testers, country=USSegmentRule matchesSkippedon
2ravi not in segment, country=INAttributeEqualsRule matchesSkippedon
3alex not in segment, country=BRNo rule matchesStable bucket lands in first 25on
4missing flag for chenNo flag loadedNot evaluatedcaller fallback off

The run shows the precedence order. Explicit segment targeting beats attribute targeting, attribute targeting beats rollout, and missing flags are handled by the service fallback instead of throwing.

Complexity Analysis

OperationTimeSpaceNote
evaluate flagO(R + W)O(1)R targeting rules are checked until a match; W rollout weight entries are scanned only when no rule matches.
upsert flagO(1 + L)O(1)Concurrent map update plus notifying L listeners.
build user contextO(A + S)O(A + S)Immutable copies for A attributes and S segment keys keep evaluation inputs safe.
percentage bucketO(K)O(1)K is the combined length of flag key and user key fed into the hash.

Evaluation is intentionally local to one flag. If a flag accumulates hundreds of rules, index segment rules or compile rules into a decision tree, but keep the public evaluator contract unchanged.

Extensibility

New rule type

Add a class such as PlanRule, EmailDomainRule, or TimeWindowRule that implements TargetingRule. FlagEvaluator stays untouched.

Typed variations

Replace string variations with a VariationValue object that can hold boolean, number, string, or JSON-like payloads. The rule contract can still return the selected variation key.

External segments

Resolve segment membership before building UserContext, or introduce a SegmentProvider used by SegmentRule. Keep remote I/O outside the hot evaluator path.

Advanced rollout algorithms

Swap PercentageRollout for experiments, prerequisite flags, or mutually exclusive experiments. The evaluator still treats rollout as the final strategy after rules.

Persistence and audit

Store immutable flag versions in a repository and have FeatureFlagService upsert snapshots from that repository. Listener events can include old and new versions.

Alternative Designs

Expression tree rules

Represent targeting as an expression tree with AND, OR, NOT, and leaf predicates. The evaluator recursively evaluates the tree and returns a variation at matching leaves.

Tradeoffs

More powerful for product teams, but harder to explain and validate in a machine-coding interview than an ordered rule chain.

Central decision table

Flatten rules into table rows with columns for segment, country, plan, and variation. Evaluation scans rows and chooses the first row whose predicates all match.

Tradeoffs

Easy for simple admin UIs, but adding new predicate types often changes the table schema and evaluator logic.

Remote SDK cache

Move storage and evaluation data to a remote control plane. Application SDKs keep an in-memory cache and evaluate locally, refreshing via streaming updates.

Tradeoffs

Better production latency and availability, but introduces distributed cache consistency, streaming retries, and stale-data handling.

Common Mistakes

  • ×

    Putting a switch on rule type inside FlagEvaluator, which violates the open-closed goal of the problem.

  • ×

    Using random numbers for percentage rollout, causing a user to flip between variations across requests.

  • ×

    Ignoring rule order and combining all matches, which makes explicit targeting unpredictable.

  • ×

    Returning null for missing flags instead of a clear caller fallback or default variation.

  • ×

    Letting UserContext be mutable while multiple threads evaluate flags against it.

  • ×

    Not validating that rules and rollouts return only allowed variations.

  • ×

    Calling observers before storing the new flag snapshot, causing listeners to refresh stale data.

Follow-up Interview Questions

QHow would you support multivariate experiments?

Keep variations as named keys and let PercentageRollout hold multiple weighted variations such as A 50%, B 25%, C 25%. Metrics and experiment analysis are separate systems fed by evaluation events.

QHow do you keep rollout stable when increasing from 10% to 25%?

Use deterministic buckets. Users in buckets 1..10 stay enabled, and buckets 11..25 are newly enabled. Never reshuffle all users on each percentage change.

QWhere do prerequisite flags fit?

Add a PrerequisiteFlagRule that asks an evaluator for another flag's result, with cycle detection and a max depth. The main engine still sees it as a targeting rule.

QHow would you scale this across services?

Serve flag snapshots from a control plane, let SDKs evaluate locally, and push updates through streaming or polling. The same evaluator can run inside every SDK process.

QHow do you audit flag changes?

Version every flag update, record actor, timestamp, old value, new value, and change reason. Emit observer events after persistence so audit trails and cache invalidations are consistent.

Production Considerations

Persistence and versioning

Store flag definitions as immutable versions. Evaluators should receive a complete snapshot so a request never observes half of an update.

SDK caching

Production SDKs should evaluate locally from a cache and refresh by streaming updates or polling. Remote calls during every request would add latency and outage coupling.

Observability

Emit evaluation counts by flag, variation, rule id, and fallback reason. Alert on sudden missing-flag spikes or unexpected variation distribution.

Governance

Track owners, descriptions, expiry dates, and cleanup status. Stale flags become hidden complexity and can keep dead code alive for years.

Security

Admin mutations need RBAC, approvals for production environments, and audit logs. Client-side SDKs must receive only flags safe to expose to end users.

What Interviewers Look For

  • Did you clearly separate flag data, rule strategies, evaluation engine, and service boundary?

  • Can you add a new rule type without editing FlagEvaluator?

  • Is percentage rollout deterministic per flag and user?

  • Did you explain rule ordering and first-match-wins semantics?

  • Did you include cache invalidation or observer notifications for flag updates?

  • Did you name the missing-flag and disabled-flag fallback behavior?

Quiz

0/5 answered

  1. 1.Why should **FlagEvaluator** not switch on concrete rule type?

  2. 2.What does first-match-wins mean for targeting rules?

  3. 3.Why hash **flagKey:userKey** for rollout instead of only **userKey**?

  4. 4.Which pattern is represented by **FlagChangeListener**?

  5. 5.What should happen when a flag is disabled?

Practice Variants

Add prerequisite flags

Advanced

Implement a PrerequisiteFlagRule that matches only when another flag resolves to a required variation. Add cycle detection to prevent recursive evaluation loops.

Add rule groups with AND and OR

Intermediate

Support compound targeting such as country=IN AND plan=PRO, while preserving the same TargetingRule contract for the evaluator.

Add flag version audit events

Advanced

Extend the service to persist old and new flag versions, then send observers a structured change event with actor, timestamp, and reason.

Flashcards

Cheat Sheet

Entities: FeatureFlag, TargetingRule, PercentageRollout, UserContext, FlagEvaluator, FeatureFlagService, FlagChangeListener.

Patterns: Strategy for rules, Chain of Responsibility for ordered first-match evaluation, Observer for change notifications, Singleton for the demo service boundary.

Evaluation order: missing flag -> caller fallback; disabled flag -> default; targeting rules -> first matching variation; rollout -> deterministic bucket; otherwise default.

Determinism: hash flagKey:userKey into bucket 1..100; cumulative rollout weights choose the variation.

Extensibility: add a new rule by implementing TargetingRule. Do not edit FlagEvaluator for country, plan, device, segment, or time-window rules.

Production: SDK local cache, immutable flag versions, audit log, RBAC for admin changes, metrics by flag/rule/variation, cleanup workflow for stale flags.

References