Skip to main content

segments/<segment-key>.toml

Segments are reusable, named audiences. Flag rules reference segments by key (or use an inline predicate for one-off audiences). This page is the complete reference for a segment file.

The predicate expression language lives in predicates; percentage-bucket segments live in buckets.


Skeleton

schema_version = "0.1"

[segment]
description = "..."

# At least one of [segment.predicate] or [segment.bucket] MUST be present.

[segment.predicate]
# Predicate expression — see predicates.md

[segment.bucket]
# Percentage-bucket configuration — see buckets.md

The filename stem is the segment key. There is no separate segment.key field.

A segment file MUST contain:

  1. A top-level schema_version.
  2. A [segment] table.
  3. Either [segment.predicate], or [segment.bucket], or both.

A segment defining neither raises E011.


[segment] fields

FieldTypeRequiredDefaultValidationDescription
descriptionstringno""Free-form proseWho is in this segment. The linter emits I003 when absent or empty.
predicatepredicate expressionno (but see below)See predicatesBoolean expression evaluated against the evaluation context.
bucketbucket tableno (but see below)See bucketsDeterministic bucket-range definition.

At least one of predicate or bucket MUST be present (E011 otherwise).

No key field. Schema 0.1 removed segment.key; the segment key is the filename stem and the only source of truth. Declaring key = "..." under [segment] is E016.

Segment key semantics

The filename stem is the wire-level identifier referenced from:

  • Flag rules: segment = "<key>".
  • Other segments' predicates: { segment = "<key>" }.

The stem MUST match the key pattern [a-z][a-z0-9_-]* (max 63 chars). Violations raise E032 and the file is dropped before per-file lint.

description

Free-form prose explaining the audience. The linter emits info diagnostic I003 when the field is absent or empty.

A good description answers: who is in this segment, and why? ("First 10% of users hashing into the checkout-redesign-2025 rollout space, used to gradually expand the new checkout flow.")


Three segment shapes

1. Predicate only

# segments/internal-or-beta-users.toml
schema_version = "0.1"

[segment]
description = "Internal employees or users enrolled in the beta program"

[segment.predicate]
or = [
{ attribute = "user.segment", op = "eq", value = "internal" },
{ segment = "beta-users" },
]

The entity is a member iff the predicate evaluates to true. Predicates may include attribute checks, segment references, and compound expressions; see predicates.

2. Bucket only

# segments/checkout-redesign-rollout-10.toml
schema_version = "0.1"

[segment]
description = "First 10% of users for the checkout redesign rollout"

[segment.bucket]
entity_id_attribute = "user.id"
salt = "checkout-redesign"
start = 0
end = 999

The entity is a member iff its hash bucket falls inside [start, end]. See buckets.

3. Both (predicate + bucket hybrid)

# segments/beta-users-variant-a-bucket.toml
schema_version = "0.1"

[segment]
description = "First 10% of beta users (constrained 10% within the beta cohort)"

[segment.predicate]
segment = "beta-users"

[segment.bucket]
entity_id_attribute = "user.id"
salt = "checkout-redesign"
start = 0
end = 999

When both fields are present, the entity is a member iff both the predicate and the bucket match. This is the canonical pattern for "variant A for the first N% of users, but only inside a stable audience."


Cross-segment references

A segment's predicate MAY reference another segment via { segment = "<key>" }. References are resolved recursively: the referenced segment is evaluated against the same evaluation context, and the result feeds into the parent predicate.

The linter enforces:

  • Existence. Every { segment = "<key>" } reference MUST resolve to a segment file in the same flag namespace's segments/ directory; otherwise E005.
  • Acyclicity. Circular references are forbidden. The linter detects cycles via depth-first search and emits E012 on each cycle's entry segment. Examples:
    • a → a (self-loop): E012 on a.
    • a → b → a: E012 on the first segment in the cycle reached during traversal.
    • Two disjoint cycles: each cycle reports E012 on its respective entry.
    • A reference to a missing segment: E005 only (the broken edge is not a cycle).

Negation via segment reference

A predicate-only segment can negate another segment's membership:

# segments/non-internal-users.toml
[segment]

[segment.predicate]
not = { segment = "internal-users" }

Members of non-internal-users are exactly those entities not in internal-users. Useful for layered rule sets that want to exclude an audience without copying the predicate.


Segments are flag-namespace-local

Segments are scoped to the flag namespace they live in. A flag in flag namespace payments cannot reference a segment in flag namespace growth; cross-namespace dependencies are not supported. The linter operates on one flag namespace at a time and treats every cross-namespace reference as a missing segment (E005).

To reuse audiences across flag namespaces, define the predicate logic redundantly in each one, or build out a shared flag namespace and have both teams contribute to it.


Common diagnostics

ScenarioDiagnostic
[segment] table missing entirelyE025
segments/<stem>.toml filename stem violates patternE032 (file dropped before lint)
Neither predicate nor bucket declaredE011
description absent or emptyI003
predicate references a segment that doesn't existE005
predicate forms a cycle (direct or transitive)E012
predicate exceeds 5 levels of compound nestingW005
predicate contains a malformed atom or compoundE015
predicate is an empty table (no keys)E015
predicate atom uses in / not_in with values = []E033
Segment is defined but never referencedW013
bucket missing required keys or has invalid rangeE006
bucket has no salt fieldW004
bucket and predicate both present, both well-formedaccepted
Unknown field at [segment]E016

  • Name segments after the audience they describe, not the rollout step. internal-employees is more reusable than experiment-42-treatment-a.
  • Use predicate-only segments for stable audiences — employees, beta cohorts, geographic regions.
  • Use bucket segments for percentage rollouts. Always set an explicit salt (eliminates W004 and lets you stabilize bucket assignments across segment renames).
  • Use predicate + bucket for "the first N% within a stable audience."
  • Compose segments rather than copy predicates. A segment that references three other segments via an or is easier to maintain than three separate flags each repeating the same predicate. The lint cycle check keeps you safe from the obvious failure modes.

See also

  • predicates — the predicate language used inside [segment.predicate].
  • buckets[segment.bucket] fields, hash math, entity_id_attribute semantics.
  • flag — how flag rules reference segments.
  • diagnosticsE005, E006, E011, E012, E015, E025, E032, E033, W004, W005, W013.