Skip to content

Evaluation semantics

How Gates decides. Every answer comes with a reason telling you which step decided — use it for debugging, never for branching in product code.

Feature flags

Checked in order — first hit wins:

  1. Environment switch — flag off in this environment? Serve off.
  2. Overrides — a user id or email domain pinned to on/off in the dashboard. First match wins.
  3. Conditions — if the flag has conditions and all of them match, serve on. If they don't match, move on to the rollout (they don't serve off by themselves).
  4. Rollout — the percentage decides. 100% is on for everyone, 0% off for everyone, anything between buckets the user by userId.
reasonMeaning
flag_offOff in this environment.
overrideA user-id or domain override matched.
conditionsAll conditions matched — served on.
rolloutThe percentage decided.
missing_user_idPartial rollout but no userId to bucket by.
unsupported_ruleThe flag uses a rule this SDK version doesn't know — served off. Upgrade the SDK.

Experiments

Checked in order:

  1. Not running → not_running
  2. Off in this environment → environment_off
  3. Audience doesn't match → not_in_audience (audience deleted → audience_not_found)
  4. No userIdmissing_user_id
  5. In the holdout → holdout
  6. Outside the traffic percentage → not_in_traffic
  7. Otherwise → assigned, with a variant { id, name, isControl }

Every assignment also carries an iteration number. Restarting an experiment bumps it and reshuffles everyone — record it with your events so results never mix populations.

Conditions

Traits are compared as strings, the same way on every platform.

OperatorMatches when
isTrait equals the value exactly.
is_notTrait differs (or is unset).
containsTrait contains the value, ignoring case.
gt / ltGreater/less than. Numeric if both sides are numbers, otherwise text order.
is_setTrait is present and non-empty.

Stickiness

Rollouts and assignments aren't random — they're a hash of the user id and a salt stored with the gate:

  • The same user gets the same answer every time, on every platform.
  • Renaming a gate never reshuffles who's in.
  • Restarting an experiment uses a new salt, so everyone reshuffles — that's the point of a restart.

Fail-safety

When the SDK meets something it doesn't understand, it answers safely instead of guessing:

  • A rule added after your SDK version shipped → the whole gate answers unsupported_rule (flag off, experiment unassigned).
  • An experiment's audience was deleted → audience_not_found, nobody is assigned.

Lucerna Developer Docs