---
title: Targeting rules
description: How an evaluation reaches a value — the default, the rules in order, percentage rollouts and the context they read.
type: explanation
product: flags
summary: The order an evaluation decides in, and the context it decides from
updated: 2026-09-28
order: 0
---

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

<PropertyTable
	properties={[
		{
			name: 'value',
			type: 'value',
			required: true,
			description: 'The value served when the rule matches, of the flag’s type.',
		},
		{
			name: 'percentage',
			type: 'int32',
			description: 'Serve value to evaluations whose bucket is below this, from 0 to 100; default 100. The rest fall through to the next rule.',
		},
		{
			name: 'bucket_attribute',
			type: 'string',
			description: 'The 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.',
		},
		{
			name: 'description',
			type: 'string',
			description: 'What the rule is for.',
		},
		{
			name: 'conditions',
			type: 'TargetingCondition[]',
			description: '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

<PropertyTable
	properties={[
		{
			name: 'attribute',
			type: 'string',
			required: true,
			description: 'user_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.',
		},
		{
			name: 'operator',
			type: 'ConditionOperator',
			required: true,
			description: 'The comparison.',
		},
		{
			name: 'value',
			type: 'value',
			required: true,
			description: 'The 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:

<KeyValue
	items={[
		{ key: 'user_id', value: 'The signed-in user; the default bucketing subject.' },
		{ key: 'anonymous_id', value: 'A stable id of a device or visitor; the bucketing subject when there is no user_id.' },
		{ key: 'attributes', value: '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:

<PropertyTable
	properties={[
		{
			name: 'config_flag',
			type: 'string',
			description: 'The flag’s id.',
		},
		{
			name: 'value',
			type: 'value',
			description: 'The value; null when the flag does not exist.',
		},
		{
			name: 'reason',
			type: 'EvaluationReason',
			description: 'Why this value. One of targeting_match, split, default, disabled, not_found.',
		},
		{
			name: 'rule_index',
			type: 'int32',
			description: 'The 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.

<RelatedDocs
	links={[
		{
			href: '/docs/flags/segments',
			label: 'Segments',
			description: 'The audience an IN_SEGMENT condition names, and how to change it.',
		},
		{
			href: '/docs/flags/changes',
			label: 'Change history',
			description: 'Every committed change to a rule, and who made it.',
		},
		{
			href: '/docs/api/config_flags',
			label: 'The config_flags collection',
			description: 'Every method, its scope and its examples.',
		},
	]}
/>
