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 when | Use a feature variable when |
|---|---|
| The value has its own lifecycle and targeting | The value belongs to one feature |
| The value is shared across features or services | The value follows a feature variation |
| The value does not need percentage rollout | The 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#
description: Support email shown to userstags: - alltype: stringThe 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:
description: Identifier accepted from several systemsoneOf: - type: string - type: integerdefaultValue: anonymousEach 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:
description: Shared checkout settingstags: - checkouttype: objectproperties: currency: type: string paymentMethods: type: array items: type: stringrequired: - currency - paymentMethodsdefaultValue: currency: EUR paymentMethods: - visa - mastercardTo use a reusable schema, set schema instead of inline fields such as type, properties, or items:
description: Checkout settingsschema: checkoutSettingsdefaultValue: currency: EUR paymentMethods: - visaOverrides#
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.
description: Support email shown to userstype: stringoverrides: production: - key: netherlands conditions: attribute: country operator: equals value: nl - 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 - paypalThe 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 informatieThe 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 messagetype: stringdefaultValue: WelcomedisabledValue: Header unavailablerequiredFeatures: - showHeader - feature: navigationExperiment variation: treatmentWhen only one enabled feature is needed, use the direct string form:
requiredFeatures: showHeaderEach 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:
description: Checkouttags: [checkout]bucketBy: userIddisabledVariationValue: controlvariations: - value: control weight: 50 - value: treatment weight: 50rules: production: - key: everyone segments: '*' percentage: 0overrides: 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 unavailableA 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.
description: Checkout datafiletag: checkoutincludeVariables: - 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/:
variable: supportEmailassertions: - description: Uses the Netherlands override environment: production context: country: nl expectedEvaluation: reason: variable_override_rule variableOverrideKey: netherlands variableOverrideIndex: 0Matrix 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:
$ npx featurevisor test --entityType=variableDefinition controls#
Global variables support archived, deprecated, and promotable. Individual overrides also support promotable so promotions can protect values owned by a destination set.

