Skip to Content
FeatureGetting started

These pages document @evanion/feature 0.1.1, the version on npm.

Getting started

The examples on this page are regions of libs/feature/docs/examples.md and run under nx test @evanion/feature.

By the end of this page, you will have a store of features resolving for a customer, a rollout check, and a toggle that reports its transitive impact.

Prerequisites

To start using @evanion/feature, you need a compatible runtime and the package installed in your project.

npm install @evanion/feature

How to define features

To manage rollouts and dependencies, you first define the intent of your features in @evanion/feature. A feature consists of enabled (the shop’s intent), rules (which determine if that intent reaches a customer), and dependsOn (the flags that must resolve first).

import { } from '@evanion/feature'; const = ([ { : 'new-checkout', : true, : [ { : 'after-launch', : [{ : 'now', : 'after', : '2026-10-01T00:00:00Z' }], : { : 25, : 'targetingKey' }, }, ], }, { : 'express-pickup', : true, : ['new-checkout'] }, ]); const = new ('2026-11-01T00:00:00Z'); .('express-pickup', { : 'cust-0042', }); // -> true .('express-pickup', { : 'cust-0107', }); // -> false

The createFeatures function validates this graph and returns a store. To determine if a feature is active for a specific user, you use isEnabled. This function requires a targetingKey to bucket the customer and a now timestamp to evaluate time windows.

For example, cust-0042 and cust-0107 receive different results because a 25% rollout places them in different buckets. These answers remain consistent across subsequent requests.

How to check a customer’s rollout bucket

Because @evanion/feature uses a hash rather than a random draw, a customer’s assignment is stable everywhere and forever—a property required by shops that a coin flip cannot provide.

import { } from '@evanion/feature'; ('cust-0101', 10, 'new-checkout'); // -> true ('cust-0042', 10, 'new-checkout'); // -> false
inRollout('cust-0101', 10, 'new-checkout')

true

Retype the id. The same customer always lands in the same place, and about one in ten is in.

By retyping the ID, you can see that roughly one in ten IDs are “in,” while neighboring IDs land anywhere. The same ID always returns the same result. For details on the underlying buckets and how percentage changes affect them, see Rollouts.

How to debug why a flag is off

While isEnabled returns a boolean, the resolve function in @evanion/feature provides a detailed decision for each flag. The reason field identifies which of the five possible cases occurred; for instance, dependency-off specifies the parent flag that blocked the feature. Using the store created above:

.({ : 'cust-0107', })['express-pickup']; // -> { key: 'express-pickup', enabled: false, reason: 'dependency-off', blockedBy: 'new-checkout', cause: { key: 'new-checkout', reason: 'no-rule-matched', rule: 'after-launch' } }

In this case, cust-0107 falls outside the new checkout’s 25% rollout. Consequently, the after-launch rule never matches, and express pickup is disabled with blockedBy naming the parent flag. You can find the other four reasons in Decisions.

How to toggle a feature

The toggle function is the only call in @evanion/feature that writes. It updates the enabled state of a stored definition and reports which dependent features are disabled as a result.

import { } from '@evanion/feature'; const = ([ { : 'new-checkout', : true }, { : 'express-pickup', : true, : ['new-checkout'] }, { : 'demo-night-booking', : true, : ['express-pickup'] }, ]); .('new-checkout', false); // -> { ok: true, key: 'new-checkout', enabled: false, willDisable: ['express-pickup', 'demo-night-booking'], mayDisable: [] }

The willDisable list is transitive and follows dependency order: for example, demo night booking depends on express pickup, which depends on the new checkout. This list can be used to power a confirmation dialog or be ignored by a script.

No other functions write to the store. resolve and plan are pure functions of the configuration, the context, and the clock; thus, a time window closing will change an answer without modifying any stored data.

You now have two dependent flags resolving for a customer and a toggle that reports its impact. To learn how to write your own definitions, including every field, the precedence between them, and the conditions a rule can match on, see Configuration.

Last updated on