Diogo Spínola I'm a learning enthusiast, web engineer, and blogger who writes about programming stuff that draws my attention.

Using feature flags with Unleash and Node.js

6 min read 1746

Using Feature Flags With Unleash and Node.js

What are feature flags?

In programming, a flag is a variable that causes a program to execute a path depending on its value.

Take a look at the example below:

if (flag === true) {
  // feature 1
} else {
  // feature 2
}

Flags are usually environment variables that are called when the server starts and used to do things like set the level of details in logs between development and production environments, establish the default language of an application, etc. To switch those variables, you’d need to make a change in a file or database and restart the server so the new settings are applied.

A feature flag is a variable that you can switch at any moment and the change will reflect in the program without the need to restart it.

When to use feature flags

Picture this: You just launched a new feature on your website — a button to change the background to a random color. You’ve tested and installed it and you can’t wait for your users to experience it.

After some time, a user interacts with the feature and finds that the text color is being changed instead of the background. Catastrophe strikes, and now you must revert the functionality until it’s fixed. This usually leads to an urgent restart to apply the code and remove the feature, followed by another one to apply the fix.

Ideally, a restart would only occur when new features or fixes are added, preferably without urgency. This is where feature flags come in handy. Feature flags enable you to disable faulty features on the fly, eliminating the step of reverting the functionality and restarting to remove it.

How feature flags work

To show feature flags in action, we’ll use the aforementioned example: a website with a faulty button that is switching the text color instead of the background color.

Project structure

src/
  |-public // folder with the content that we will feed to the browser
    |-js
      new-feature.js
    index.html    
  package.json
  server.js // our Node.js server

package.json

{
  "name": "feature-flags-101",
  "version": "1.0.0",
  "description": "Setup of feature flags with unleash",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "server-debug": "nodemon --inspect server.js"
  },
  "author": "daspinola",
  "license": "MIT",
  "devDependencies": {
    "nodemon": "2.0.4"
  },
  "dependencies": {
    "express": "^4.17.1"
  }
}

server.js

const express = require('express')
const path = require('path')
const app = express()

app.use(express.static(path.join(__dirname, 'public')))

app.get('/', function(req, res) {
  res.sendFile(path.join(__dirname, 'public/index.html'))
})

app.listen(7000, function () {
  console.log(`Listening on port ${7000}!`)
})

public/index.html

<html>
  <head>
    <title>Feature flags 101</title>
  </head>
  <body>
    <div>
      <span id="span-welcome-text">My awesome website has a new button!!!</span>
      <button id="button-switch-background-color">Background Color</button>
    </div>
    <script src="/js/new-feature.js"></script>
  </body>
</html>

public/js/new-feature

document.addEventListener('DOMContentLoaded', init, false);

function init() {
  const buttonSwitchColor = document.getElementById('button-switch-background-color')

  buttonSwitchColor.addEventListener('click', () => {
    const body = document.querySelector('body')
    body.style.color = getRandomColor()
  })

  // Code from: https://stackoverflow.com/questions/1484506/random-color-generator
  function getRandomColor() {
    var letters = '0123456789ABCDEF'
    var color = '#'
    for (var i = 0; i < 6; i++) {
      color += letters[Math.floor(Math.random() * 16)]
    }
    return color
  }
}

In the package.json there is nodemon, so you can have the server perform a restart whenever you make any change and express to get a quick HTTP server setup, which we have in server.js.

index.html contains the website with the faulty button. new-feature.js is where the color-changing functionality is defined.

We made a custom demo for .
No really. Click here to check it out.

Color Change Functionality Changing Text Color Instead of Background Color

Using feature flags with Unleash

Now that we’ve built our faulty website, let’s see how the same buggy feature could’ve been introduced under the feature flag.

Unleash is a feature toggle system that enables you to create a UI where the flag can be switched on and off easily.

Setting up Unleash

To set up Unleash, we need three things:

  1. Postgres, which is where Unleash saves the feature flags status
  2. unleash-server, which provides a UI and endpoint to query for the status of the flags
  3. unleash-client-node, a library to connect to Unleash server and know the status of the flag

One way to have Postgres is to execute it with Docker.

docker run --name unleash-postgres -e POSTGRES_PASSWORD=pass -d -p 5432:5432 postgres

Once it’s running, you can connect it with Postbird and create the “unleash” database.

Create Unleash Database

Now it’s time to install unleash-server by running npm i unleash-server and creating a new file in the project.

unleash-server.js

const unleash = require('unleash-server');

unleash
  .start({
    databaseUrl: 'postgres://postgres:pass@localhost:5432/unleash',
    port: 4242,
  })
  .then(unleash => {
    console.log(
      `Unleash started on http://localhost:${unleash.app.get('port')}`,
    );
  });

package.json

{
  "name": "feature-flags-101",
  "version": "1.0.0",
  "description": "Setup of feature flags with unleash",
  "main": "server.js",
  "scripts": {
    "start": "node server.js",
    "server-debug": "nodemon --inspect server.js",
    "unleash-server": "node unleash-server.js"
  },
  "author": "daspinola",
  "license": "MIT",
  "devDependencies": {
    "nodemon": "2.0.4"
  },
  "dependencies": {
    "express": "^4.17.1",
    "unleash-server": "^3.5.4"
  }
}

By executing the command npm run unleash-server on the browser, on localhost:4242 there should now be a UI.

Add Feature Flags

Now let’s create one for the feature we’re going to develop. For example:

Create New Feature Toggle

With Postgres in place and unleash-server running, you can now connect to the server to check the status of the feature flag using npm install unleash-client and change the server like so:

server.js

const express = require('express')
const path = require('path')

// The unleash client makes requests every 10 seconds or so for updates on the status of the flags so there is no need to restart the server if they change
const { initialize, isEnabled } = require('unleash-client');
const instance = initialize({
    url: 'http://localhost:4242/api/',
    appName: 'feature-flags',
    instanceId: 'feature-flags-101',
});

const app = express()
app.use(express.static(path.join(__dirname, 'public')))

app.get('/', function(req, res) {
  res.sendFile(path.join(__dirname, 'public/index.html'))
})

// This is the endpoint we're going to call in our front end to check if a flag is available or not
app.get('/feature-flag/:name?', function(req, res) {
  const flagName = req.params.name
  res.send({
    flagName,
    isEnabled: isEnabled(flagName)
  })
})

// We add this way so our server only starts when the flags are available
instance.on('ready', () => {
  app.listen(7000, function () {
    console.log(`Listening on port ${7000}!`)
  })
})

public/js/new-feature.js

document.addEventListener('DOMContentLoaded', init, false);

async function init() {
  const body = document.querySelector('body')
  const spanWelcomeText = document.getElementById('span-welcome-text')
  const showNewButton = await fetch('/feature-flag/random-background-color-button')
    .then(response => response.json())

  if (showNewButton.isEnabled) {
    const newButton = document.createElement('button')

    newButton.id = 'button-switch-background-color'
    newButton.innerHTML = 'Background Color'
    newButton.addEventListener('click', () => {
      body.style.color = getRandomColor()
    })
    body.appendChild(newButton)

    spanWelcomeText.innerHTML = 'My awesome website has a new button!!!'

    // Taken from the useful reply in: https://stackoverflow.com/questions/1484506/random-color-generator
    function getRandomColor() {
      var letters = '0123456789ABCDEF'
      var color = '#'
      for (var i = 0; i < 6; i++) {
        color += letters[Math.floor(Math.random() * 16)]
      }
      return color
    }
  }
}

public/index.html

<html>
  <head>
    <title>Feature flags 101</title>
  </head>
  <body>
    <div>
      <span id="span-welcome-text">My awesome website will have a new button!!!</span>
    </div>
    <script src="/js/new-feature.js"></script>
  </body>
</html>

The main idea of this updated code is that the button is only built if the server replies saying that the flag is enabled. Otherwise, the UI is ready to be shown to the user instead of a broken feature.

When the client reports that a new feature is broken, the Unleash UI should be opened and the feature disabled. After 10 seconds and a refresh of the page, the client will no longer see the broken button

Feature Flag Disabled in Unleash

Fallback Behavior

Check the official documentation for more ways to install Unleash.

Fixing the bug

If you look at the problem with the feature flag disabled for the users, you will eventually notice that body.style.color = getRandomColor() should actually be body.style.backgroundColor = getRandomColor().

After fixing, testing, and redeploying the functionality, the feature flag can be activated.

Flag Activated in Unleash

Background Color Change Feature

Modes

An added bonus of having flags is that you can control how changes are propagated to your user base.

Three of the most useful modes in Unleash are:

  • DefaultStrategy, which is the same as saying “apply this flag to everyone”
  • gradualRolloutSessionId, which shows the feature you want to enable to a certain percentage of users who have an active session
  • gradualRolloutRandom, which randomly activates when a user accesses the page. This is especially useful if what is under the feature flag is not visible to the user — for example, a new version of an endpoint in an API

You can check all the available options in the official docs.

Should you use feature flags?

Before we wrap up, let’s break down some of the pros and cons of using feature flags in Node.js.

Pros

  • There is no need to stop an application to roll back a troublesome feature
  • Once set up, feature flags are easy to keep adding to a project
  • With feature flags, you can test a feature among a small group and collect feedback

Cons

  • Feature flags must be removed after a feature is proven to be stable; you should always plan a removal date so the project doesn’t accumulate dead code or flags under flags
  • When not set up correctly, feature flags can create a bad user experience. An example would be a form that loads before a flag that discontinues fieds, leading to users filling in fields that are no longer needed and will vanish after a few seconds
  • There is potential to ship twice as much code since you keep the old and new code available to be triggered at any moment, especially when it comes to deploying experimental versions of an existing feature

Final remarks

When used appropriately, feature flags can eliminate much of the stress associated with launching a new release of your application. If a problem arises, all you need to do is click a button to restore things to how they were and give yourself time to fix the issue with minimal downtime.

Using feature flags can also lead to great insights if you use metrics to compare old features versus new features. To use our example, if choosing a custom background color keeps users on the page, you should consider retaining that feature in your app. Just don’t forget to remove it after some time has passed; nobody wants unnecessary bundles of code lying around.

You can access all the code used in this tutorial on my GitHub page.

200’s only Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
Diogo Spínola I'm a learning enthusiast, web engineer, and blogger who writes about programming stuff that draws my attention.

Leave a Reply