Evaluation order
The exact precedence the SDKs follow to resolve a flag.
When you call enabled() or getValue(), the SDK resolves the flag against the
cached config in a fixed order. Every SDK follows these exact steps — this
precedence is part of the cross-language parity contract.
Throughout, enabled value means true for a boolean flag, or the flag's
enabled_value (falling back to default_value if unset) for a non-boolean
(string / number / json) flag. Default value is the flag's default_value.
The five steps
-
Flag disabled → default value. If the flag is off, return the default value immediately. Nothing else is considered.
-
No user context → rollout-or-default. If you didn't pass a
user, there's no one to match rules against or to bucket. Return the enabled value only if the rollout is 100 (serve-everyone); otherwise the default value. -
Rule groups → enabled value. With a user present, check the targeting groups in order. The first group whose conditions all match (AND within a group; OR across groups) returns the enabled value. A matched user gets the feature regardless of the rollout percentage.
-
Rollout percentage. If no rule matched and the user has a
user_id(orid), bucket them:sha256("{user_id}:{flag_key}") % 100. Return the enabled value if the bucket is less thanrollout_pct, otherwise the default. (Atrollout_pct = 100, every bucket 0–99 is below 100, so everyone is included.) -
Otherwise → default value. If nothing matched — no rule, and no usable
user_idto bucket (so only a 100% rollout would have applied) — return the default value.
In short
- Off beats everything → default.
- A matching rule beats the rollout → enabled.
- The rollout decides the rest → enabled if in-bucket, else default.
- No way to decide (no user / no id, rollout below 100) → default.
The default value is the safe fallback at every dead end, which is why it should always be the conservative choice (usually your existing behaviour).
Why no-context and no-id behave the same
Steps 2 and 5 both fall back to "enabled only if rollout is 100." A rollout needs an id to bucket; without one, the only sensible interpretation of a partial rollout is "not this caller," while a full rollout still means everyone. This keeps a flag at 100% behaving like a simple on/off even when no user is passed.
Next
- Percentage rollouts — the bucketing in depth.
- Rule operators — what makes a condition match.