Skip to content
Console
Menu

Queues

Workflows

Getting Started

Authentication

KV Store

Targeting rules

The order an evaluation decides in, and the context it decides from

An evaluation is one small decision made in a fixed order. Nothing about it is implicit: the flag's spec holds a default, an ordered list of rules, and a switch, and the answer comes from reading them in one order. That is what makes a rollout something you can reason about after the fact.

#The order of the decision

  1. enabled. When it is false, every evaluation returns default_value and the rules are not read at all: the kill switch.
  2. The rules, in order. The first rule whose conditions all hold decides the evaluation, and a rule's percentage can hand part of that match on to the next rule instead.
  3. default_value, when no rule claims the evaluation.

#The default is a real answer

default_value is required, so there is always something to serve — that is the first property of the design: an evaluation can never fail for want of a value. A flag with no rules serves the default to everyone, and the default is also what a request a rule cannot route falls back to. The answer carries the reason default in that case, and disabled when the switch is off, so a value that looked surprising in a log says which of the two happened.

#A rule

FieldTypeWhat it is
valuevaluerequiredThe value served when the rule matches, of the flag’s type.
percentageint32Serve value to evaluations whose bucket is below this, from 0 to 100; default 100. The rest fall through to the next rule.
bucket_attributestringThe context attribute hashed for the bucket: user_id, anonymous_id, or an attribute path; default user_id, else anonymous_id. An evaluation without it never matches a rule below 100 percent.
descriptionstringWhat the rule is for.
conditionsTargetingCondition[]Conditions over the evaluation context; all must hold. None matches every evaluation.

Rules are ordered and first match wins, so the list is a decision procedure rather than a set: a rule with no conditions at the top shadows every rule below it. That is also why conditions and percentage share one object — a rollout to one audience is one rule, and the audience is the conditions.

#Percentage rollouts

percentage is 0 to 100 and defaults to 100. It decides by bucket, not by chance: the evaluation's bucket_attribute is hashed into a stable bucket, so the same subject falls the same way on every request, and 10 percent stays 10 percent of subjects rather than 10 percent of requests. The evaluations outside the percentage do not fall to the default — they fall through to the next rule, which is what makes a gradual rollout a list of rules rather than a single one.

bucket_attribute names what to hash: user_id, anonymous_id, or a path into the context's attributes. It defaults to user_id, and to anonymous_id when there is no user. An evaluation that does not carry the attribute the bucket needs never matches a rule below 100 percent, so a rule that buckets by user_id does not match a request that has none.

#Conditions

FieldTypeWhat it is
attributestringrequireduser_id, anonymous_id, or a path into the context’s attributes, dot-separated (plan, device.os). Required, except with IN_SEGMENT and NOT_IN_SEGMENT.
operatorConditionOperatorrequiredThe comparison.
valuevaluerequiredThe value compared with; a list for IN and NOT_IN, a string for the string, regular expression and semantic version operators.

The operators are the comparisons you would expect — equals, not_equals, greater_than, greater_or_equal, less_than, less_or_equal, contains, not_contains, starts_with, ends_with, in, not_in, matches, and the four semantic version comparisons semver_greater_than, semver_greater_or_equal, semver_less_than and semver_less_or_equal. Two of them compare against a segment instead of a literal: in_segment and not_in_segment name a segment by id, which is the one case where a condition needs no attribute of its own.

All the conditions of a rule must hold, and a rule with no conditions matches every evaluation. matches is a regular expression, which is why a rule that leans on it is worth describing in its description.

#The evaluation context

Every evaluation carries an EvaluationContext, and that is the only input a rule reads:

user_id
The signed-in user; the default bucketing subject.
anonymous_id
A stable id of a device or visitor; the bucketing subject when there is no user_id.
attributes
Anything else rules compare, for example plan, country, app_version.

Nothing else about the request is consulted. A rule that reads plan reads it from attributes, so the answer depends on what the caller said the subject is, not on where the call came from.

#What evaluate answers

evaluate takes the environment, the context, and the ids of the flags to evaluate; an empty list evaluates every flag of the environment, and an id the environment does not have answers not_found rather than failing the call.

Each flag in the answer carries four things:

FieldTypeWhat it is
config_flagstringThe flag’s id.
valuevalueThe value; null when the flag does not exist.
reasonEvaluationReasonWhy this value. One of targeting_match, split, default, disabled, not_found.
rule_indexint32The index in spec.rules of the rule that matched, for TARGETING_MATCH and SPLIT.

reason is the field that makes a rollout debuggable: it says why this value rather than only which value, and it separates an id that does not exist from a flag that is switched off. rule_index goes further and names which rule decided it, in the order you wrote, for the two reasons a rule produces.