Featurevisor

Multi-set workflows

Promotions

Promotions copy definitions from one set to another, so you can move work along release lanes like dev, staging, and production.

Pre-requisites

Promotions only apply to projects that use sets. Each lane is modelled as its own set, for example dev, staging, and production, each owning its own attributes, segments, features, targets, and tests.

featurevisor.config.js
module.exports = {
sets: true,
tags: ['all'],
}

The source and destination must be different existing sets. Featurevisor lints both sets before calculating a promotion and stops without writing files if either set has errors.

Previewing a promotion

The promote command previews what would be copied from one set to another. Nothing is written to disk until you ask for it:

Command
$ npx featurevisor promote --from=dev --to=staging

This shows the definitions that would be created or updated in the destination set. When you promote a feature, Featurevisor also brings along its dependencies, like the segments and attributes it references.

To include unchanged entries in the preview:

Command
$ npx featurevisor promote --from=dev --to=staging --showUnchanged

Applying a promotion

Pass --apply to write the destination files:

Command
$ npx featurevisor promote --from=dev --to=staging --apply

Applied definitions are ordinary project files. Review the Git diff, then lint and test the destination set before committing them:

Commands
$ npx featurevisor lint --set=staging
$ npx featurevisor test --set=staging

Filtering features

Filters choose the initial features for a promotion. Featurevisor then includes their complete dependency closure, including required features, exclusion groups, segments, nested segments, attributes, schemas, and relevant tests.

To promote everything needed to build one target's datafile, pass the source target key:

Command
$ npx featurevisor promote --from=dev --to=staging --target=web

This uses the target's tag or tags, includeFeatures, and excludeFeatures selectors with the same semantics as a datafile build. It also promotes the target definition itself. Target context can simplify the eventual datafile, but promotion keeps the full definitions needed by the selected features.

To select features carrying a tag:

Command
$ npx featurevisor promote --from=dev --to=staging --tag=web

You can also select features by key using glob patterns:

Command
$ npx featurevisor promote --from=dev --to=staging --includeFeatures="checkout*"

To start with all features and exclude matching keys:

Command
$ npx featurevisor promote --from=dev --to=staging --excludeFeatures="experimental*"

Filters can be combined. Positive selectors such as --target, --tag, and --includeFeatures use AND semantics. --excludeFeatures is applied last and takes precedence. Dependency closure may add required features or exclusion group members even when they do not match the initial filters.

--target, --tag, --includeFeatures, and --excludeFeatures are repeatable when more than one selector is needed.

If the final initial selection contains no features, the command fails so the mistake is visible early. Pass --allowEmpty if an empty result is acceptable. An unknown --target or --tag always fails and lists the available source values.

Conflicts

When a definition already exists in the destination set with different values, that is a conflict. The --conflicts option controls how conflicts are resolved:

  • source: the source set wins and overwrites the destination (default)
  • destination: the destination keeps its existing values
  • fail: stop with an error instead of overwriting
Command
$ npx featurevisor promote --from=dev --to=staging --conflicts=fail

Protecting definitions during promotion

By default, definitions, feature rules, and test assertions are promotable. Use promotable: false when part of a destination set must remain under that set's control, such as a production rollout percentage, a production-only assertion, or a target with lane-specific selectors.

Supported locations

promotable is supported in these locations:

LocationPlacementPromotion behaviour
AttributeTop level of the attribute fileProtects the whole destination attribute
SegmentTop level of the segment fileProtects the whole destination segment
FeatureTop level of the feature fileProtects the whole destination feature
Feature ruleOn an individual entry under rulesOmits a source rule or preserves its matching destination rule
GroupTop level of the group fileProtects the whole destination group
Reusable schemaTop level of the schema fileProtects the whole destination schema
TargetTop level of the target fileProtects the whole destination target
Feature or segment test specTop level of the test spec fileProtects the whole destination test spec
Feature or segment test assertionOn an individual keyed entry under assertionsOmits a source assertion or preserves its matching destination assertion

It is not supported as promotion protection on individual matrix cases, child assertions, group slots, segment conditions, force entries, variable overrides, or nested schema properties. Linting rejects it in most unsupported locations. Protect the parent assertion when all of its matrix cases or child assertions must stay together.

Whole definitions

Set promotable: false at the top level of a definition:

sets/dev/features/checkoutFlow.yml
description: Checkout flow
promotable: false
tags:
- all
rules:
- key: everyone
segments: '*'
percentage: 100

The result depends on whether the matching definition already exists in the destination:

SourceDestinationResult
promotable: falseExistsKeep the destination unchanged
Promotablepromotable: falseKeep the destination unchanged
promotable: falseMissingCreate it and retain promotable: false

The protection takes precedence over --conflicts. The matching definition is determined by its entity key, which comes from its file path.

This top level behaviour applies to attributes, segments, features, groups, reusable schemas, targets, and feature or segment test specs.

For example, a lane-specific test spec can protect all of its assertions together:

sets/production/tests/features/checkoutFlow-production.spec.yml
feature: checkoutFlow
promotable: false
assertions:
- description: Keep the production rollout disabled
at: 50
context:
userId: production-user
expectedToBeEnabled: false

Individual feature rules

Rules have item-level protection because each rule has a stable key. A source rule with promotable: false is omitted from the promoted feature. An existing destination rule with promotable: false is preserved when a source rule with the same key is promoted:

sets/dev/features/checkoutFlow.yml
description: Checkout flow
rules:
- key: internal-testing
promotable: false
segments: internal
percentage: 100
- key: everyone
segments: '*'
percentage: 50

For rules grouped by environment, matching happens within the same environment. If the project has no environments, matching happens in the flat rules array.

When a filtered promotion calculates dependencies, an omitted source rule does not add the segments used only by that rule. A segment may still be promoted if another selected definition depends on it.

Individual test assertions

Assertions can use stable keys for item-level protection. If one assertion in a test spec has a key, every assertion in that spec must have a unique key. An assertion that sets promotable must always have a key.

sets/production/tests/features/checkoutFlow.spec.yml
feature: checkoutFlow
assertions:
- key: production-rollout
promotable: false
description: Keep the production rollout disabled
at: 50
context:
userId: production-user
expectedToBeEnabled: false
- key: general-checkout
description: Check the general checkout experience
at: 80
context:
userId: general-user
expectedToBeEnabled: true

A source assertion with promotable: false is omitted. An existing destination assertion with it is preserved when a source assertion with the same key is promoted. If the destination test spec is missing, only promotable source assertions are copied.

Keys must be present in both source and destination test specs when assertion protection is involved. Promotion stops with guidance if only one set uses keys, since matching assertions by position would be unsafe. Existing test specs without assertion keys keep the earlier whole-array promotion behaviour.

A matrix belongs to its parent assertion. Protecting the assertion protects all of its expanded matrix cases together. The same applies to child assertions. Individual matrix cases and child assertions cannot have separate promotion protection.

Assertion keys also give the Catalog stable labels and permalinks for assertions and their expanded matrix cases.

Allowed promotion flows

By default, any set can be promoted to any other set. To restrict which promotions are allowed, define promotionFlows in your configuration:

featurevisor.config.js
module.exports = {
sets: true,
tags: ['all'],
promotionFlows: [
{ from: 'dev', to: 'staging' },
{ from: 'staging', to: 'production' },
],
}

With the configuration above, promote --from=dev --to=production fails because that flow is not listed. Promoting from dev to staging, or from staging to production, is allowed.

Audit files

Pass --audit to write a record of a promotion. This is useful alongside --apply to keep a trail of what changed:

Command
$ npx featurevisor promote --from=dev --to=staging --apply --audit=markdown

Audit files are written only when --apply is present, under .featurevisor/promotions/. Use --audit or --audit=json for machine-readable output, or --audit=markdown for a human-readable summary.

Learn more

  • Sets: split a project into independent trees
  • Environments: different rules per feature within a single tree
  • State files: where Featurevisor keeps generated state
Previous
Sets
Next
Flags