The purpose of this repository is to demonstrate and experiment with existing and proposed functions of OpenFeature. These experiments were written in TypeScript and focus on NodeJS, but the basic concepts translate to most implementation languages.
If you're new to OpenFeature, or feature-flags in general, we recommend you start by running the demo UI, along with the JSON-file provider. A guided tour will walk you through some basic concepts. To run the demo:
- Run
npm ci
- Run the JSON-file provider:
npm run json-demo
- In a new terminal session run the UI:
npm run ui
- Open localhost:4200 in your browser!
You can also run the demo with any provider of your choice, by running that provider instead of the JSON-file provider (see demo providers). You can also generate scaffolding to create your own custom provider.
A video recording of the demo flow is available:
These are the APIs and interfaces that Application Author (who doesn't particularly care about the underlying feature flag implementation) uses OpenFeature to control "pivot points" in their code.
// initialize OpenFeature and create a client
openfeature.registerProvider(
new MyFeatureFlagSystemProvider({ apiKey: 'my-secret-api-key' })
);
const client = openfeature.getClient();
// evaluate a boolean flag
const myFlagEnabled = await client.isEnabled('my-boolean-flag-key', false); // false is the default enabled state
// get a string flag value
const myStringFlagValue = await client.getStringValue(
'my-string-flag-key',
'hello world!' //default value
);
// get a numeric flag value
const myNumericFlagValue = await client.getNumberValue(
'my-numeric-flag-key',
1337 // 1337 is the default value
);
// get a object flag value
const myObject = await client.getObjectValue<MyObject>(
'my-object-flag-key',
new MyObject() // MyObject instance is the default value
);
// provide additional attributes to flag evaluation
const requestData = {
ip: req.headers['x-forwarded-for'] || req.socket.remoteAddress,
};
const myStringFlagValue = await client.getStringValue(
'my-string-flag-key',
'hello world!',
requestData
);
// get the details of a flag evaluation
const { flagKey, value, variant, reason, errorCode, executedHooks } =
await client.getStringDetails('my-string-flag-key', 'hello world!');
// attach additional handlers to the flag evaluation lifecycle (can also be attached globally and on client, to impact all evaluations)
const myStringFlagValue = await client.getStringValue(
'my-string-flag-key',
'hello world!',
requestData,
{
hooks: [
{
name: 'log',
// "before" can be used for logging, telemetry configuration, and manipulating attributes
before: (hookContext) => {
console.log('before');
},
// "after" can be used for logging, telemetry cleanup, and even validating and manipulating the returned value
after: (
hookContext,
evaluationDetails: FlagEvaluationDetails<string>
) => {
console.log('after');
},
},
],
}
);
To run the demo scenarios described on this page, please make sure you have the following prerequisites configured on your system.
- Git
- NodeJS 14 or later
- Docker
You can then clone the repo and install the dependencies.
- clone the repo:
git clone https://github.com/open-feature/playground.git
- Install dependencies:
npm ci
Available demos:
- No-op
- Environment Variable Provider
- JSON-file Provider Demo
- Split Provider
- CloudBees Feature Management Provider
- LaunchDarkly Provider Demo
- Flagsmith Provider Demo
- GO Feature Flag Demo
- OpenTelemetry and Zipkin
Providers are an important part of OpenFeature. They're responsible for performing the flag evaluation and must adhere to the feature flag API. To get started, run the following command:
npm run provider-generator
You'll need to provide a name for the generator. After that, the output will contain the path to the new provider class and a start command.
A core design principle of OpenFeature is a simple, understandable API. OpenFeature's API needs to be flexible enough to handle all the common use cases of feature flags while being easy to work with. In order to support this, a common API needs to be defined that's capable of supporting core feature flagging use cases. The method names and signatures still need to be agreed upon by the community, but a basic implementation can be found here.
Inspired by OpenTelemetry, this experimental SDK registers itself globally. Typically doing this is discouraged but, in this case, it provides interesting flexibility. It allows library maintainers to include OpenFeature in their project, even if they're running a different, but compatible version of OpenFeature. An example of how this could be used can be found in the Fibonacci library.
OpenFeature allows developers to register a provider. Providers are responsible for using the flag identity and context to determine the state of the feature. If no providers are registered, the flags will no-op and return the default value.
To see this in action, we'll run the API app found here. Notice that we're using OpenFeature but not registering a provider. Let's run the app and see what happens.
- Run
npm run no-op-demo
- Open http://localhost:3000/message, http://localhost:3000/hex-color/markup, or http://localhost:3000/calculate?num=40 in your browser
- Optionally, run the UI as described in the introductory demo
That's it! You should see Welcome to the api!. Unfortunately, that's all you can do without registering a provider. Thankfully, as we'll see in the next section, that part is easy.
Another key design principle of OpenFeature is compatibility with existing open source and commercial feature flag offerings. It should be possible to register a feature flag provider that's responsible for handling the flag evaluation. Only a single provider can be registered at a time and not providing one will cause flag evaluations to return the default value. This can be tested by running the application without registering a provider.
NOTE: You may notice that the demos below don't register providers directly in the app. This is done before the main app starts using the
-r
node cli argument. The config can be found here.
The environment variable provider is a simple demo showing how environment variables could be used make flag evaluations. Its purpose is to show how a provider could be implemented.
Follow these steps to run the demo:
- copy
.env.example
to.env
- Run
npm run env-var-demo
- Open http://localhost:3000/message, http://localhost:3000/hex-color/markup, or http://localhost:3000/calculate?num=40 in your browser
- Optionally, run the UI as described in the introductory demo
You should see Welcome to the api! just as before. Now, change the value of
new-welcome-message
to true and restart the app. It should show Welcome
to the next gen api! in your browser. Now we're getting somewhere, but
it's still a bit too basic to be useful. The next demo will show how we could
register a commercial feature flag tool using an existing SDK.
The JSON provider is a simple demo showing how environment variables could be used make flag evaluations. Its purpose is to demonstrate a slightly more complex local provider, with some ability to do basic dynamic flag evaluation.
Follow these steps to run the demo:
- Run
npm run json-demo
- Open http://localhost:3000/message, http://localhost:3000/hex-color/markup, or http://localhost:3000/calculate?num=40 in your browser
- Optionally, run the UI as described in the introductory demo
You should see Welcome to the api! just as before. Now, change the value of
new-welcome-message
to true and restart the app. It should show Welcome
to the next gen api! in your browser. Now we're getting somewhere, but
it's still a bit too basic to be useful. The next demo will show how we could
register a commercial feature flag tool using an existing SDK.
The Split provider shows how an existing SDK, in this case Split's NodeJS SDK, can be used in OpenFeature. This is a simple example that doesn't cover every use case that Split offers, but the goal would be to support as many features as possible.
Follow these steps to run the demo:
- Copy
.env.example
to.env
- Add a Split.io service-side API
key to the
SPLIT_KEY
in the.env
file. - Create a new split called
new-welcome-message
with the default treatments - Create a new split called
fib-algo
with the treatment values:recursive
,memo
,loop
,binet
, anddefault
. - Create a new split called
hex-color
with the treatment values:CC0000
,00CC00
,0000CC
,chartreuse
. - Run
npm run split-demo
- Open http://localhost:3000/message, http://localhost:3000/hex-color/markup, or http://localhost:3000/calculate?num=40 in your browser
- Optionally, run the UI as described in the introductory demo
A CloudCees Feature Management provider demo.
Follow these steps to run the demo:
- Copy
.env.example
to.env
- Add a CloudBees app key to the
.env
file. - Create a new boolean flag called
new-welcome-message
. - Create a new flag called
fib-algo
with the values:recursive
,memo
,loop
,binet
, anddefault
. - Create a new flag called
hex-color
with the values:CC0000
,00CC00
,0000CC
,chartreuse
. - Run
npm run cloudbees-demo
- Open http://localhost:3000/message, http://localhost:3000/hex-color/markup, or http://localhost:3000/calculate?num=40 in your browser
- Optionally, run the UI as described in the introductory demo
A LaunchDarkly provider demo.
Follow these steps to run the demo:
- Copy
.env.example
to.env
- Add a LaunchDarkly SDK key to the
.env
file. - Create a new boolean flag called
new-welcome-message
. - Create a new feature flag called
fib-algo
with the values:recursive
,memo
,loop
,binet
, anddefault
. - Create a new feature flag called
hex-color
with the values:CC0000
,00CC00
,0000CC
,chartreuse
. - Run
npm run launchdarkly-demo
- Open http://localhost:3000/message, http://localhost:3000/hex-color/markup, or http://localhost:3000/calculate?num=40 in your browser
- Optionally, run the UI as described in the introductory demo
A Flagsmith provider demo (supports v1 and v2).
Follow these steps to run the demo:
- Copy
.env.example
to.env
- Add a Flagsmith Environment ID (v1) or a Environment key (v2) to the
.env
file. - Create a new boolean feature called
new-welcome-message
. - Create a new feature called
fib-algo
with the values:recursive
,memo
,loop
,binet
, anddefault
. - Create a new feature called
hex-color
with the values:CC0000
,00CC00
,0000CC
,chartreuse
. - Run
npm run flagsmith-v1-demo
ornpm run flagsmith-v2-demo
- Open http://localhost:3000/message, http://localhost:3000/hex-color/markup, or http://localhost:3000/calculate?num=40 in your browser
- Optionally, run the UI as described in the introductory demo
A go-feature-flag provider demo.
- You should start by running an instance of
go-feature-flag-relay-proxy
.
# download relay proxy configuration file
curl https://gist.githubusercontent.com/thomaspoignant/777c23351a07dc88d01bf162d2496115/raw/94eb003c7e98f00398dd62a9aea67b1f5f21fd42/goff-proxy.yaml -o goff-proxy.yaml
# download an example of flag configuration file
curl https://gist.githubusercontent.com/thomaspoignant/fefe16973c1272ff0682212564874c90/raw/d96c13f57132816f3989a3dd5caf5b582fe6565b/flags.yaml -o flags.yaml
# launch go-feature-flag relay proxy (here using the docker image)
docker run -p 1031:1031 -v $(pwd)/:/goff/ thomaspoignant/go-feature-flag-relay-proxy:latest
- Run
npm run go-feature-flag-demo
provider demo - Open http://localhost:3333/message, http://localhost:3333/hex-color/markup, or http://localhost:3333/calculate?num=40 in your browser
- Optionally, run the UI as described in the introductory demo
Now, wouldn't it be nice if you could visually see the impact an algorithm had on a request? That's where OpenTelemetry support comes in.
Supporting OpenTelemetry natively in OpenFeature provides a number of advantages. The most obvious benefit is distributed traces would contain feature flag information. This would make it easy to determine the impact a feature or features had on a single request. Other features, such as baggage and request scoped context, may be useful options in the future.
OpenTelemetry is not required to use OpenFeature. Similar to OpenFeature, if a provider is not registered, the library no-ops. That means you are able to take advantage of all the great features of OpenTelemetry if you already use it, with minimal overhead if you don't.
- Start Zipkin in Docker:
docker run --rm -d -p 9411:9411 --name zipkin openzipkin/zipkin
- Open http://localhost:9411/ in your browser
- Start one of the demos above or run
npm run no-op-demo
- Open Zipkin and search for a trace
Experiment with different Fibonacci algorithms and hitting the API with these values: