Definitions in depth
Environments
Environments give one feature different rules in each place it runs, such as staging and production, and build a datafile for each. They are optional. The default project has none, and every feature has one list of rules.
Environments, sets or neither#
Start with neither unless you know you need more:
- Neither is the default. Each feature has one list of rules and the project builds one set of datafiles. Test changes before they merge, and use a percentage rollout in production.
- Environments keep the rules for staging and production side by side in each feature file. Choose them when one change should be reviewed for all environments at once.
- Sets keep a separate tree of definitions per release lane, and changes move from one lane to the next through promotions. Choose them when a lane should be able to differ completely, as described below.
The choice can change later. convert moves a project between the three, and checks that every feature and global variable builds the same.
Add environments#
For a new project, start from the example with environments in an empty directory:
npx @featurevisor/cli@latest init --example=with-environmentsFor an existing project, convert it. Converting lists the environments in the configuration and rewrites your definitions to match:
module.exports = { environments: ['staging', 'production'],}Leave environments out entirely for a project without them. An empty list fails.
Give each environment its own rules#
With environments, rules groups its rule lists by environment. This keeps checkout from the Quick start as it is in production, and fully on in staging:
description: Checkout redesignbucketBy: userIdrules: staging: - key: everyone conditions: "*" percentage: 100 production: - key: netherlands conditions: netherlands percentage: 100 - key: everyone conditions: "*" percentage: 25- A feature that leaves out an environment has no rules there, so it is disabled, though
forceentries for that environment still apply. - An empty list such as
staging: []fails lint. Leave the key out instead. forceandexposeare grouped by environment the same way, and so are a global variable'soverrides.- Attributes, segments, schemas and targets are shared by every environment.
Build a datafile per environment#
Building writes each target's datafile once per environment:
tree datafilesdatafiles├── REVISION├── production│ └── featurevisor-all.json└── staging └── featurevisor-all.jsonbuild --environment=staging builds only staging and leaves production untouched, but still moves the shared revision forward. Build every environment before you publish the whole datafiles directory.
Load the right datafile#
Each deployment of your application loads its own environment's file, such as production/featurevisor-all.json. The SDK evaluates whatever datafile it is given, and never picks an environment from the context.

Test and evaluate per environment#
Every feature assertion names its environment:
feature: checkoutassertions: - description: Dutch users, production environment: production at: 90 context: country: nl expectedToBeEnabled: true - description: Everyone, staging environment: staging at: 90 context: country: de expectedToBeEnabled: trueThe CLI's runtime commands take the same choice, such as npx featurevisor evaluate --environment=production. See Testing and the CLI.
Switch an existing project#
convert changes an existing project's shape, rewriting every definition, test spec, state file and the configuration to match. Give a project without environments staging and production:
npx featurevisor convert --to=environmentsIt previews by default, and writes only with --apply. Before writing, it checks that a converted copy lints, passes its tests and builds every feature and global variable the same in each environment.
To remove environments, name the one whose behaviour to keep:
npx featurevisor convert --to=flat --keep=productionAfter applying, add or remove --environment in your commands, and point applications at the datafile paths the preview names. Converting shows what each conversion writes and checks.
Older scaffolds also used tag: all in targets/all.yml. Keep it to include only tagged definitions, or remove it to include everything. Target selection does not depend on environments.
Release lanes with sets#
Some teams treat dev, staging and production as separate release lanes rather than environments of one definition. Each lane is a set with its own complete tree, so a feature can differ completely between lanes, and changes move forward only when promoted. The example for this layout keeps its historical name:
npx @featurevisor/cli@latest init --example=environmentsIt enables sets instead of environments, and uses promotionFlows to keep promotions moving forward one lane at a time:
module.exports = { sets: true, promotionFlows: [ { from: 'dev', to: 'staging' }, { from: 'staging', to: 'production' }, ],}Each lane has its own attributes/, segments/, features/, targets/ and tests/ under sets/<lane>/. Rules are plain lists, because there are no environments. The same feature can be fully on in dev and still off in production:
description: New checkout flowbucketBy: userIdrules: - key: everyone conditions: "*" percentage: 100description: New checkout flowbucketBy: userIdrules: - key: everyone conditions: "*" percentage: 0Building writes datafiles/dev/featurevisor-all.json, datafiles/staging/featurevisor-all.json and datafiles/production/featurevisor-all.json. When dev is ready, promote it to staging:
npx featurevisor promote --from=dev --to=staging --applypromotionFlows rejects promoting straight from dev to production. See Sets and Promotions.
In the example, staging sits between the two: its checkoutFlow is on for the internal segment and off for everyone else.

Turn environments into sets#
To make an existing project's environments release lanes, convert it:
npx featurevisor convert --to=setsEach environment becomes a set, and each feature keeps that environment's rules there. convert --to=environments goes back, when the sets share their attributes, segments, schemas and targets. See Turn environments into sets and Go back to environments.

