Environments - Featurevisor
Featurevisor

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.

Add environments

For a new project, start from the example with environments in an empty directory:

Command
npx @featurevisor/cli@latest init --example=with-environments

For an existing project, list the environments in the configuration, and convert your definitions as described in Switch an existing project:

featurevisor.config.js
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:

features/checkout.yml
description: Checkout redesign
bucketBy: userId
rules:
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 force entries for that environment still apply.
  • An empty list such as staging: [] fails lint. Leave the key out instead.
  • force and expose are grouped by environment the same way, and so are a global variable's overrides.
  • Attributes, segments, schemas and targets are shared by every environment.

Build a datafile per environment

Building writes each target's datafile once per environment:

Command
tree datafiles
Output
datafiles
├── REVISION
├── production
│ └── featurevisor-all.json
└── staging
└── featurevisor-all.json

build --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.

features/checkout.yml holds staging rules, everyone at 100%, and production rules, netherlands at 100% and everyone at 25%. The build writes staging/featurevisor-all.json for the staging deployment and production/featurevisor-all.json for the production deployment.

Test and evaluate per environment

Every feature assertion names its environment:

tests/features/checkout.spec.yml
feature: checkout
assertions:
- description: Dutch users, production
environment: production
at: 90
context:
country: nl
expectedToBeEnabled: true
- description: Everyone, staging
environment: staging
at: 90
context:
country: de
expectedToBeEnabled: true

The CLI's runtime commands take the same choice, such as npx featurevisor evaluate --environment=production. See Testing and the CLI.

Switch an existing project

Changing environments changes the shape expected throughout the project, not only its build output. Keep the rest of the configuration, including tags and target selectors.

To remove environments, for example from a project made with an older default scaffold:

  1. Choose which environment's behaviour to keep, such as production, and remove environments from featurevisor.config.js.
  2. In every feature, replace the rules map with that environment's rule list, and do the same for force and expose. In global variables, replace the overrides map with that environment's list.
  3. Remove environment from feature, segment and variable test assertions, scenario roots, matrices and nested cases. Keep tests for the behaviour you kept.
  4. Remove --environment from commands, then run npx featurevisor lint, npx featurevisor test and npx featurevisor build.
  5. Deploy the new output and point applications at featurevisor-all.json instead of production/featurevisor-all.json.

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.

To add environments to a flat project, do the reverse: list their names, move rule lists and global override lists under the environment keys, convert force and expose, and add an environment to tests. Then rebuild, and point deployments and applications at the paths that include the environment.

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:

Command
npx @featurevisor/cli@latest init --example=environments

It enables sets instead of environments, and uses promotionFlows to keep promotions moving forward one lane at a time:

featurevisor.config.js
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:

sets/dev/features/checkoutFlow.yml
description: New checkout flow
bucketBy: userId
rules:
- key: everyone
conditions: '*'
percentage: 100
sets/production/features/checkoutFlow.yml
description: New checkout flow
bucketBy: userId
rules:
- key: everyone
conditions: '*'
percentage: 0

Building 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:

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

promotionFlows 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.

Three sets read bottom up. In sets/dev, checkoutFlow is on for everyone. In sets/staging, it is on for internal users only. In sets/production, it is off for everyone. Promotions move from dev to staging and from staging to production.
Previous
Tags