Identity-based value overrides, flag dependencies, and prerequisite chains for complex feature gating
Overrides & Prerequisites
Overrides and prerequisites add two powerful capabilities to flag evaluation: forcing specific values for individual users, and gating flags behind other flags.
Evaluation Order
With overrides and prerequisites, the full evaluation order becomes:
- Disabled check — if the flag is disabled, return default with
DISABLED - Overrides — identity match on
targetingKey, highest priority wins - Prerequisites — all prerequisite flags must evaluate to their expected values
- Targeting rules — evaluate conditions (with schedule gating)
- Variant selection — percentage-based variant assignment
- Default value — fallback with
DEFAULT
Overrides take precedence over everything except the disabled check. Prerequisites are checked before targeting rules — if any prerequisite fails, the flag returns its default value.
Overrides
Overrides let you force a specific value for individual users, bypassing all targeting rules and variant logic. Common use cases:
- QA testing — give testers the new experience without changing targeting rules
- Incident response — force a user off a broken feature immediately
- Customer support — enable a feature for a specific customer who reported an issue
- Dogfooding — ensure internal team always sees new features
Defining Overrides
{
"key": "checkout-v2",
"enabled": true,
"defaultValue": false,
"overrides": [
{
"name": "QA Team",
"description": "QA testers always see new checkout",
"identifiers": ["qa-user-1", "qa-user-2", "qa-user-3"],
"value": true,
"priority": 10
},
{
"name": "Blocked User",
"description": "Customer experiencing issues",
"identifiers": ["user-8472"],
"value": false,
"priority": 20
}
],
"targeting": [...]
}Override Properties
| Property | Type | Description |
|---|---|---|
name | string | Human-readable name for this override group |
description | string | Optional explanation of why this override exists |
identifiers | string[] | List of targetingKey values to match |
value | any | The value to return for matched users |
priority | number | Higher priority overrides are checked first |
Priority Resolution
When a user matches multiple overrides, the highest priority override wins:
{
"overrides": [
{ "name": "Global block", "identifiers": ["user-42"], "value": false, "priority": 1 },
{ "name": "VIP access", "identifiers": ["user-42"], "value": true, "priority": 10 }
]
}User user-42 gets true because the VIP override (priority 10) outranks the global block (priority 1).
Override Evaluation Result
When an override matches, the evaluation returns:
{
"value": true,
"reason": "OVERRIDE"
}When Overrides Don't Apply
- Flag is disabled —
DISABLEDalways takes precedence - No
targetingKeyin context — overrides require a targeting key to match - Empty overrides array — falls through to prerequisites and targeting
Prerequisites
Prerequisites let you create flag dependencies — "Flag B is only evaluated if Flag A returns a specific value." This is useful for:
- Feature gating chains — new checkout requires the payment API flag to be enabled
- Staged rollouts — a feature depends on an infrastructure flag being active
- Kill switches — disable a parent flag to cascade-disable all dependent features
Defining Prerequisites
{
"key": "checkout-animations",
"enabled": true,
"defaultValue": false,
"prerequisites": [
{
"flagKey": "checkout-v2",
"expectedValue": true
},
{
"flagKey": "animation-engine",
"expectedValue": true
}
],
"targeting": [
{
"id": "all-users",
"conditions": [
{ "property": "plan", "operator": "not_equals", "value": "free" }
],
"value": true
}
]
}This flag (checkout-animations) only evaluates its targeting rules if both checkout-v2 and animation-engine evaluate to true.
Prerequisite Properties
| Property | Type | Description |
|---|---|---|
flagKey | string | Key of the prerequisite flag |
expectedValue | any | Value the prerequisite must evaluate to |
How Prerequisites Are Evaluated
- Each prerequisite flag is resolved using the same evaluation context
- The prerequisite flag goes through its own full evaluation (including its own prerequisites, if any)
- If the prerequisite's result doesn't match
expectedValue, the dependent flag returns its default value with reasonPREREQUISITE_FAILED - All prerequisites must pass — they're evaluated with AND logic
Prerequisite Failure Response
{
"value": false,
"reason": "PREREQUISITE_FAILED"
}If the prerequisite flag doesn't exist, the response includes error details:
{
"value": false,
"reason": "PREREQUISITE_FAILED",
"errorCode": "FLAG_NOT_FOUND",
"errorMessage": "Prerequisite flag 'checkout-v2' not found"
}Recursive Prerequisites
Prerequisites are resolved recursively. If Flag C depends on Flag B, which depends on Flag A:
Flag A (no prerequisites) → evaluates normally
Flag B (requires Flag A = true) → checks A first
Flag C (requires Flag B = true) → checks B, which checks A
Flaggr handles this automatically via evaluateAsync(). Be careful not to create circular dependencies — a flag that depends on itself (directly or indirectly) will fail.
Sync vs Async Evaluation
Prerequisites require resolving other flags, which may involve storage lookups. Flaggr provides two evaluation paths:
| Method | Prerequisites | Use case |
|---|---|---|
FlagEvaluator.evaluate() | Skipped (no async I/O) | Hot-path client evaluation |
FlagEvaluator.evaluateAsync() | Fully resolved | Server-side API evaluation |
Server-side API routes use evaluateAsync() automatically when a flag has prerequisites.
Combining Overrides and Prerequisites
The evaluation order ensures predictable behavior:
{
"key": "premium-feature",
"overrides": [
{ "name": "QA", "identifiers": ["qa-1"], "value": true, "priority": 10 }
],
"prerequisites": [
{ "flagKey": "premium-backend", "expectedValue": true }
],
"targeting": [...]
}qa-1always getstruevia the override — prerequisites are not checked- Other users must pass the
premium-backendprerequisite before targeting rules apply
This lets QA testers access features even when prerequisites aren't satisfied yet.
Best Practices
- Keep prerequisite chains short — deep chains increase evaluation latency
- Use overrides for temporary exceptions, not permanent targeting
- Document override reasons in the
descriptionfield - Review overrides periodically — stale overrides add noise and confusion
- Name overrides after the purpose, not the person ("QA Testing" not "alice")
- Don't use circular prerequisites — Flag A → Flag B → Flag A will fail
Related
- Targeting Rules — Conditions, operators, and evaluation order
- Cohorts — Audience segmentation for targeting
- Scheduled Rules — Time-based rule activation
- Progressive Rollouts — Staged deployment with safety checks