flags
A simple feature flagging library.
100 lines of code, no dependencies. Written in ES6+.

flags lets you quickly switch features on or off based on a context. This is
useful to test features for specific users (such as flagging on new functionality
in a web application by reading the response context), dark-launching code,
and a/b testing.
import Flags from '@r/flags';
const config = {
loggedoutSearch: { loggedin: false },
oldDesign: false,
newListingStyle: { users: ['ajacksified'] },
};
const feature = new Flags(config);
feature.addRule('loggedin', function(val) { return this.loggedin === val; });
feature.addRule('users', function(names) { return names.includes(this.username); });
const userData = {
loggedin: true,
username: 'ajacksified',
};
const featureContext = feature.withContext(userData);
if (featureContext.enabled('loggedoutSearch')) {
searchControl = <LoggedOutSearch />;
console.log('show the logged out search');
}
if (featureContext.enabled('oldDesign')) {
layout = <OldLayout />;
}
if (featureContext.enabled('newListingStyle')) {
listing = <Listing2 />;
}
const enabled = featureContext.allEnabled();
const disabled = featureContext.allDisabled();
Rules and Configuration
flags configuration and rules are very simple:
- Define a name for your features; such as
'loggedoutSearch'
or 'oldDesign'
as above. These
will be the basis if your config and your feature flags for later on. - Define the rules upon which your feature will be on or off. Above, we implement
a boolean check (shoes) and an array check (username).
- Check the flag - either by sending it in (
feature.enabled('feature', { data } )
) or
by hanging on to a context-bound instance (
featureContext = feature.withContext({ data }); featureContext.enabled('feature')
)
Of note: if a rule is defined in configuration but it is not implemented, it is
assumed to be false.
Three special "pseudo-rules" implement boolean operators: and
, or
, and
not
, so that rules may stay simple and general and be combined in useful
ways. For example:
const config = {
friend: {
and: [
{ or: [ { type: 'cyborg' }, { type: 'human' } ] },
{ not: { role: 'supervillain' } }
]
}
};
You can also parse large objects to handle things such as environment variables.
To do so, you should have configuration in the format feature_name
. feature_
will be stripped from the key and lowercased:
import Flags from flags;
const config = Flags.parseConfig({
something: 'wut',
feature_flippers: { names: ['bob', 'jane'] }
});
You can supply your own function, too, and customize both key and value. In
this example, we'll expect things to start with f_
instead of feature_
.
import Flags from flags;
const config = Flags.parseConfig({
something: 'wut',
f_flippers: { names: ['bob', 'jane'] }
}, function(key, val) {
if (key.indexOf('f_') === -1) { return; }
return { key: key.slice(2), val: val };
});
You can also retrieve a list of all currently enabled or disabled rules for a
given context by calling feature.allEnabled
or feature.allDisabled
. These
can be called with a context passed in, or else will use a bound context.
Sometimes, for testing, it's nice to clone an instance that doesn't use the
original rules and configuration by reference. You can call
flagsinstance.clone()
to return a new flags instance with shallow copies of
the config, rules, and context.
Development
flags is an ES6 library. Take a look at the .babelrc
file to see what
presets we're using. To import it in ES5, use
var Flags = require ('@r/flags').default;
.
To get started:
- With node installed,
- Fork flags
- Clone your fork to your machine
- Run
npm install
to install dev dependencies (there are no regular
dependencies; dependencies are for testing and running.) - Write features, and run
npm test
and npm run lint
. Feature changes should
have tests! For ease of use, install eslint
for your favorite editor. - Check out
CONTRIBUTING.md
for more detailed info.
flags is MIT licensed. copyright 2016 reddit. See LICENSE for more info.