Concepts

How Evaluation Works

Understand evaluation order, contexts, variations, segments, rollouts, and result reasons in FlagForge

How Evaluation Works

FlagForge evaluates a feature flag against an evaluation context and returns one named variation. The pure TypeScript engine in @flagforge/core is used by both the REST API and @flagforge/sdk, so server-side and local evaluation follow the same rules.

Evaluation order

For each flag, the engine evaluates these stages in order:

  1. Disabled check. If enabled is false, the engine serves offVariationKey and returns reason OFF. No targeting rule is evaluated.

  2. Targeting rules. Rules are checked in array order. Every condition in a rule must match. The first matching rule wins.

  3. Rule result. A matching rule either serves its fixed variationKey or selects a variation from its rollout. The result reason is RULE_MATCH, including the rule ID and zero-based rule index.

  4. Fallthrough. If no rule matches, the engine serves defaultVariationKey and returns reason FALLTHROUGH.

A flag's offVariationKey, defaultVariationKey, rule variation keys, and rollout branch keys must refer to entries in variations. The engine resolves the selected key to the variation's typed value.

Note:

Keep variation keys stable. Targeting and rollout configuration refer to keys, not variation names or values. Changing a key requires updating every reference to it.

Evaluation context

An EvaluationContext identifies the subject and supplies the attributes used by conditions:

FieldRequiredDescription
keyYesStable subject identifier, such as a user ID. Percentage rollouts use it for bucketing by default.
attributesNoMap of custom attributes. Values can be strings, numbers, or booleans.

Pass the same stable key for a subject on every evaluation. If a targeting condition refers to an attribute that is absent, that condition does not match. For an inSegment condition, the condition's attribute is ignored and values contains segment keys.

typescript
import type { EvaluationContext } from "@flagforge/core";

const context: EvaluationContext = {
  key: "user-123",
  attributes: {
    plan: "pro",
    country: "US",
    accountAgeDays: 42,
  },
};

Conditions, segments, and rule precedence

A rule's conditions are combined with logical AND. A segment is a reusable set of conditions, also requiring every segment condition to match. Reference a segment with the inSegment operator in a rule condition.

Conditions support these operators:

OperatorTypical use
equals, inMatch an exact value or one of several values.
contains, startsWith, endsWithMatch string content or a string prefix or suffix.
greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqualCompare numeric values.
semverGreaterThan, semverLessThanCompare semantic version values.
matchesRegexMatch a string with a regular expression.
inSegmentMatch when the context belongs to a named segment.

Set negate: true on a condition to invert its match result. Because rules are ordered, put more specific rules before broader rules. A broad rule that matches first prevents later rules from being considered.

Rollouts and deterministic bucketing

A rollout is evaluated only after its containing rule's conditions match. Its branches assign weights to variation keys. Weights use a resolution of 100,000, so 25_000 represents 25% and 75_000 represents 75%.

By default, the engine hashes the flag key together with context.key and maps the result to a stable bucket. The same subject therefore receives the same branch for a given flag across evaluations. Set bucketBy to an attribute name to bucket on that attribute instead; if that attribute is missing, FlagForge falls back to the context key.

A rollout does not compete with other rules. The first rule whose conditions match owns the evaluation, and the rollout chooses the variation within that rule. If branch weights leave a remainder, the final branch receives it.

Result reasons and errors

evaluateFlag returns the flag key, resolved value, variation key, and reason:

ReasonMeaning
OFFThe flag was disabled and offVariationKey was selected.
RULE_MATCHA rule matched. The reason includes ruleId and ruleIndex; a rollout may have selected the variation.
FALLTHROUGHNo targeting rule matched, so defaultVariationKey was selected.
ERRORA resolution error occurred, such as a referenced variation key not existing. The result uses the off variation when available, otherwise the first variation or false; variationKey is an empty string and the reason includes an error message.

Inspect reason when diagnosing an unexpected value. A RULE_MATCH result confirms that the rule—not the default variation—determined the result.

Same semantics in the API, SDK, and core package

The API converts an environment's configuration into evaluatable flag definitions. The SDK receives those definitions when it bootstraps from GET /v1/config and evaluates them locally. Both paths use the same @flagforge/core semantics; the core engine performs no I/O and uses deterministic bucketing.

You can also reuse the engine directly in a TypeScript application:

bash
pnpm add @flagforge/core
typescript
import { evaluateFlag } from "@flagforge/core";
import type { EvaluatableFlag, EvaluationContext } from "@flagforge/core";

const flag: EvaluatableFlag = {
  key: "new-checkout",
  type: "boolean",
  enabled: true,
  variations: [
    { key: "on", value: true },
    { key: "off", value: false },
  ],
  defaultVariationKey: "off",
  offVariationKey: "off",
  rules: [
    {
      id: "pro-users",
      conditions: [{ attribute: "plan", operator: "equals", values: ["pro"] }],
      rollout: {
        branches: [
          { variationKey: "on", weight: 25_000 },
          { variationKey: "off", weight: 75_000 },
        ],
      },
    },
  ],
};

const context: EvaluationContext = {
  key: "user-123",
  attributes: { plan: "pro" },
};

const result = evaluateFlag(flag, context);
console.log(result.flagKey, result.variationKey, result.reason.kind, result.value);
// new-checkout on RULE_MATCH true  (the exact branch depends on user-123's bucket)

For local application evaluation, see the SDK Client Guide. To inspect the evaluation endpoints instead, see Server-Side Evaluation. Advanced users can find the direct evaluateFlag and evaluateAll API in the Core Evaluation Engine.