Pete Hodgson

Software Delivery Consultant

Five Minutes to Feature Flags

March 2, 2023

We’re going to add feature flagging to a node service in under five minutes using OpenFeature, the open, vendor-agnostic feature flagging SDK.

We’ll be working with a simple express server, but if you have any basic familiarity with JavaScript and node you should be able to follow along.

Hello, world

Here’s the service we’ll be working on:

server.js

import * as express from 'express'
import Router from 'express-promise-router'

const app = express()
const routes = Router();
app.use(routes);

routes.get('/', async (req, res) => {
  res.send("Hello, world!")
})

app.listen(3333)

Pretty much the most basic express server you can imagine - a single endpoint at / that returns a plaintext “Hello, world!” response.

Once the service is running (node server.js should suffice), we can test that it works:


$> curl http://localhost:3333
Hello, world!

Yep, looks good!

with cows, please

Let’s imagine that we’re adding a new, experimental feature to this hello world service. We’re going to upgrade the format of the server’s response, using cowsay!

However, we’re not 100% sure that this cowsay formatting is going to work out, so for now we’ll protect it behind a conditional:

routes.get('/', async (req, res) => {
  // set this to true to test our new
  // cow-based greeting system
  const withCow = false
  if(withCow){
    res.send(cowsay.say({text:'Hello, world!'}))
  }else{
    res.send("Hello, world!")
  }
})

By default, our service continues to work exactly as it did before, but if we change withCow to true then our response comes in an exciting new format:


$> curl http://localhost:3333
 _______________
< Hello, world! >
 ---------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

the crudest flag

That withCow boolean and its accompanying conditional check are a very basic feature flag - they let us hide an experimental or unfinished feature, but also easily switch the feature on while we’re building and testing it.

introducing OpenFeature

Managing these flags by changing hardcoded constants gets old fast though. A team that uses feature flags in any significant way soon reaches for a feature flagging framework. Let’s move in that direction by setting up the OpenFeature SDK:

$> npm install @openfeature/js-sdk 
import { OpenFeature } from '@openfeature/js-sdk'

const featureFlags = OpenFeature.getClient()

routes.get('/', async (req, res) => {
  const withCows = await featureFlags.getBooleanValue('with-cows', false)
  if(withCows){
    res.send(cowsay.say({text:'Hello, world!'}))
  }else{
    res.send("Hello, world!")
  }
})

We’ve installed and imported the @openfeature/js-sdk npm module, and used it to create an OpenFeature client called featureFlags. We then call getBooleanValue on that client to find out if the with-cows feature flag is true or false. Depending on what we get back we either show the new cow-based output, or the traditional plaintext format.

Note that when we call getBooleanValue we also provide a default value of false. Since we haven’t configured the OpenFeature SDK with a feature flag provider yet, it will always return that default value:


$> curl http://localhost:3333
Hello, world!

configuring OpenFeature

Without a feature flagging provider OpenFeature is pretty pointless - it’ll just return default values. Instead we want to connect our OpenFeature SDK to a full-fledged feature flagging system - a commercial product such as LaunchDarkly or Split, an open-source system like FlagD, or perhaps a custom internal system - so that it can provide flagging decisions from that system.

Connecting OpenFeature to one of these backends is very straightforward, but it does require that we have an actual flagging framework set up. For now, just to get started, let’s just configure a really, really simple provider that doesn’t need a backend:

import { MinimalistProvider } from '@moredip/openfeature-minimalist-provider'

const FLAG_CONFIGURATION = {
  'with-cows':true
}

const featureFlagProvider = new MinimalistProvider(FLAG_CONFIGURATION)

OpenFeature.setProvider(featureFlagProvider)
const featureFlags = OpenFeature.getClient()

This minimalist provider is exactly that - you give it a hard-coded set of feature flag values, and it provides those values via the OpenFeature SDK.

In our FLAG_CONFIGURATION above we’ve harded-coded that with-cows feature flag to true, which means that conditional predicate in our express app will now evaluate to true, which means that our service will now start providing bovine output:


$> curl http://localhost:3333
 _______________
< Hello, world! >
 ---------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

If we changed that with-cows value to false, we’d see the more boring response:


$> curl http://localhost:3333
Hello, world!

moving to a full feature-flagging system

We’ve gotten started with OpenFeature using a very simple but extremely limited provider. The beauty of OpenFeature is that we can transition to a real feature-flagging system when we’re ready, without any change to how we evaluate flags. It’s as simple as configuring a different provider. For example:

launchdarkly.js

import { init } from 'launchdarkly-node-server-sdk';
import { LaunchDarklyProvider } from '@launchdarkly/openfeature-node-server';

const ldClient = init('[YOUR-SDK-KEY]');
await ldClient.waitForInitialization();
OpenFeature.setProvider(new LaunchDarklyProvider(ldClient));
flagd.js

  OpenFeature.setProvider(new FlagdProvider({
      host: '[FLAGD_HOST]',
      port: 8013,
  }))
split.js

import { SplitFactory } from '@splitsoftware/splitio';
import { OpenFeatureSplitProvider } from '@splitsoftware/openfeature-js-split-provider';

const splitClient = SplitFactory({core: {authorizationKey:'[YOUR_AUTH_KEY]'}}).client();
OpenFeature.setProvider(new OpenFeatureSplitProvider({splitClient}));
cloudbees.js

import {CloudbeesProvider} from 'cloudbees-openfeature-provider-node'

const appKey = '[YOUR_APP_KEY]'
OpenFeature.setProvider(await CloudbeesProvider.build(appKey));

We can get started with feature flags with low investment and low risk, and once we’re ready, it’s just a few lines of code to transition to a full-featured, scalable backend.

next steps

To learn more about OpenFeature, check out their documentation here. Specifically, you can read more about how the evaluation API works, what tech stacks are supported, or read more tutorials about using OpenFeature in a variety of tech stacks.

We strive to provide a welcoming, open community. If you have any questions - or just want to nerd out about feature flags - the #OpenFeature channel in the CNCF slack is the place for you.