Featurevisor

Building blocks

Global variables

Global variables are typed configuration values that can be evaluated without first choosing a feature. Define them in the variables/ directory and include them in datafiles through tags and targets.

Featurevisor also supports variables owned by a feature.

Use a global variable whenUse a feature variable when
The value has its own lifecycle and targetingThe value belongs to one feature
The value is shared across features or servicesThe value follows a feature variation
The value does not need percentage rolloutThe value needs bucketing or gradual rollout

Global variables are deterministic for the same context and datafile. They do not have bucketBy, percentages, or traffic allocation. Put values that need gradual rollout in a feature variable.

Basic variable

variables/supportEmail.yml
description: Support email shown to users
tags:
- all
type: string
defaultValue: [email protected]

The schema fields use the same format as variablesSchema inside a feature. Supported types include boolean, string, integer, double, array, object, and json.

Use oneOf instead of a root type when a variable can use several schema branches:

variables/identifier.yml
description: Identifier accepted from several systems
oneOf:
- type: string
- type: integer
defaultValue: anonymous

Each branch accepts the normal schema fields, including enum, const, nested objects, arrays, and reusable schema references. A value must match exactly one branch. type, oneOf, and a root schema reference are mutually exclusive.

An object can define its structure inline:

variables/checkoutSettings.yml
description: Shared checkout settings
tags:
- checkout
type: object
properties:
currency:
type: string
paymentMethods:
type: array
items:
type: string
required:
- currency
- paymentMethods
defaultValue:
currency: EUR
paymentMethods:
- visa
- mastercard

To use a reusable schema, set schema instead of inline fields such as type, properties, or items:

description: Checkout settings
schema: checkoutSettings
defaultValue:
currency: EUR
paymentMethods:
- visa

Overrides

Overrides are checked in order. The first matching override supplies the value. Every override requires a stable key, at least one selector from segments, conditions, or requiredFeatures, and exactly one of value or mutate.

variables/supportEmail.yml
description: Support email shown to users
type: string
defaultValue: [email protected]
overrides:
production:
- key: netherlands
conditions:
attribute: country
operator: equals
value: nl
value: support-[email protected]
- key: default
segments: "*"

Projects without environments use the overrides array directly. Projects with environments place an array under each environment key, as shown above.

Conditions and segments cannot be used together. Use segments: "*" for an explicit catch-all override. A catch-all without required features must be last because later overrides would be unreachable.

Mutating a value

Use mutate to change part of the default value. Mutation paths are relative to the variable itself. Featurevisor resolves mutations while building the datafile, so SDKs only receive complete values.

overrides:
production:
- key: netherlands
segments: countries.netherlands
mutate:
paymentMethods:
- ideal
- paypal

The mutation notation follows the same rules as feature variable mutations.

Feature variable overrides support the same selector, value, and mutate shape. Their key remains optional for backwards compatibility, but it is recommended for new definitions.

Nested overrides

Global variable overrides can contain ordered overrides of their own. Use nesting when a broad match supplies a value and a more specific match should refine it, such as a country followed by a city.

overrides:
production:
- key: netherlands
conditions:
attribute: country
operator: equals
value: nl
mutate:
message: Welkom
overrides:
- key: amsterdam
conditions:
attribute: city
operator: equals
value: amsterdam
mutate:
cta.title: Meer informatie

The first matching sibling wins at each level. A child is checked only after its parent matches, so its selectors use AND semantics with every selector inherited from its ancestors. When no child matches, the matched parent supplies the value.

A child mutation starts from its parent’s resolved value. Featurevisor resolves the complete tree while building the datafile and emits flat, complete values ordered from the most specific child to its parent fallback. SDKs do not perform mutations during evaluation.

Override keys must be unique throughout one environment’s complete tree. Detailed evaluations expose the final variableOverrideKey. A flattened descendant also exposes its full variableOverridePath, such as ['netherlands', 'amsterdam'].

Nesting is specific to global variables. Feature variables keep their existing flat variableOverrides arrays inside rules and variations.

Required features

Use requiredFeatures when a variable should only be available while other features meet their requirements:

description: Header message
type: string
defaultValue: Welcome
disabledValue: Header unavailable
requiredFeatures:
- showHeader
- feature: navigationExperiment
variation: treatment

When only one enabled feature is needed, use the direct string form:

requiredFeatures: showHeader

Each requirement follows the same rules as feature dependencies. A string requires isEnabled() to return true. The object form accepts feature, optional enabled, and optional variation. enabled defaults to true, and all supplied checks must match.

For a global variable, disabled means that one or more required features were not satisfied. The detailed evaluation uses the reason required_features_unmet. Featurevisor returns disabledValue in this case. Set useDefaultWhenDisabled: true to return defaultValue instead. If neither behaviour supplies a value, SDK methods return null.

An override must define at least one of conditions, segments, or requiredFeatures. Conditions and segments cannot be used together. Required features can be used alone or paired with either conditions or segments. When paired, both parts must match.

When a requirement checks both enabled: false and a variation, the disabled feature must declare the expected disabledVariationValue:

features/checkout.yml
description: Checkout
tags: [checkout]
bucketBy: userId
disabledVariationValue: control
variations:
- value: control
weight: 50
- value: treatment
weight: 50
rules:
production:
- key: everyone
segments: '*'
percentage: 0
overrides:
production:
- key: dutch-checkout
conditions:
- attribute: country
operator: equals
value: nl
requiredFeatures:
- feature: checkout
variation: treatment
value: Welkom
- key: checkout-disabled
requiredFeatures:
- feature: checkout
enabled: false
variation: control
value: Checkout is unavailable

A feature's expose configuration controls whether it appears in a datafile. Requirement checks still use the SDK results directly. An omitted feature normally returns false from isEnabled(), so it fails the default enabled requirement and can satisfy an explicit enabled: false requirement.

Tags and targets

Global variables support the same project tags as features. A target includes variables whose tags satisfy its tag or tags selector. Use includeVariables and excludeVariables for glob-like matching against variable keys. Feature key selectors remain specific to features.

targets/checkout.yml
description: Checkout datafile
tag: checkout
includeVariables:
- checkout*
excludeVariables:
- checkout.internal*

Tag and key selectors use AND semantics. The resulting datafile contains matching features and global variables, required feature chains, and the runtime segments needed by their rules and overrides. Attributes and reusable schemas are used while authoring and validating the project, but they are not emitted as separate runtime entities. A target with no selectors includes every active feature and global variable.

Evaluating a variable

JavaScript applications use the global variable overload of getVariable with the variable key and optional context:

const email = f.getVariable('supportEmail', { country: 'nl' })
const settings = f.getVariable('checkoutSettings')

Feature variables continue to take a feature key followed by a variable key:

const title = f.getVariable('checkout', 'title', context)

You can inspect and evaluate all global variables loaded in an SDK instance:

const keys = f.getVariableKeys()
const values = f.getVariableEvaluations(context)

Pass selected keys as the second argument to getVariableEvaluations() when you only need part of the datafile. The returned map can be used directly as stickyVariables for server to client handoff.

Use evaluateVariable(variableKey, context) when debugging. It returns the same Evaluation type as feature evaluation, with type: "variable", no featureKey, the selected override key and index when applicable, and a reason explaining the result.

Learn more in the JavaScript SDK guide.

Testing

Variable test specs live under tests/variables/:

tests/variables/supportEmail.spec.yml
variable: supportEmail
assertions:
- description: Uses the Netherlands override
environment: production
context:
country: nl
expectedValue: support-[email protected]
expectedEvaluation:
reason: variable_override_rule
variableOverrideKey: netherlands
variableOverrideIndex: 0

Matrix placeholders are substituted recursively inside nested sticky values, defaults, expected values, and detailed evaluation expectations. Detailed expectations use documented Evaluation fields and compare nested arrays and objects by value.

Assertions support an optional stable key, promotable, optional environment, target, matrix, context, stickyVariables, defaultVariableValue, expectedValue, and expectedEvaluation. See the evaluation reason reference when asserting expectedEvaluation.reason.

Global variable assertions do not accept at because global variables do not use percentage bucketing. Required features referenced by a variable can still use bucketing, so provide a deliberate context with a stable bucket attribute such as userId, or use a force definition to test that branch.

Run variable tests with the regular test command:

Command
$ npx featurevisor test --entityType=variable

Definition controls

Global variables support archived, deprecated, and promotable. Individual overrides also support promotable so promotions can protect values owned by a destination set.

Previous
Features