A Minimalistic Approach to Feature Flagging Your Web App
Tags: webdev, programming, tutorial, javascript
Published: 2022-01-11 18:21:44

## The Purpose

It is a common occurrence that a product's web application needs to enable/disable features depending upon the circumstances.

Some use cases include:

1. Wanting to merge features for an in-progress project without showing/releasing them to customers in production
2. Wanting to preview in-progress work in staging environment
3. Pausing features without removing them from source code

Here's an example:

```jsx
// Home.jsx

function Home() {
  if (!devFlags.beta) {
     return ...;
  }

  return ...;
}
```

There are three things we need to do to make this work well:

1. Set a pattern for configuring dev flags locally
2. Set a pattern for overriding dev flags in an environment
3. Set a pattern for how to consume dev flags in the code

## Configuring Dev Flags Locally

Dev flags are just configuration, so you can think of them as a single object that can be consumed anywhere in the codebase:

```jsx
export default {
  beta: true, // if true, show the features for the beta launch
};
```

However, we will need to specify the "defaults" that control the live production experience and the "overrides" that control the local environment.

For example, imagine you want the dev flag object to return `beta: false` in production (before its release), but `beta: true` locally so that you can develop features before the release.

First, create a `dev-flags-default.js` file that controls the live production experience (the default experience):

```jsx
// dev-flags-defaults.js
export default {
  beta: false, // Don't show beta features in production
};
```

Next, create a `dev-flags-overrides.js` file that can specify the overrides fo the local experience:

```jsx
// dev-flags-overrides.js
export default {
  beta: true, // Show beta features as you develop
};
```

**NOTE:** You'll want to add this file to the `.gitignore` since the overrides should always be specific to the environment (more on that later).

Finally, expose a `dev-flags.js` file (the file that will be consumed by other files in the codebase):

```jsx
// dev-flags.js
import defaults from './dev-flags-defaults.js';
import overrides from './dev-flags-overrides.js';

export {
  ...defaults,
  ...overrides,
};
```

Now, you will be able to control what features to render based upon whether you are in a production or local environment.

## Configuring Dev Flags Based on Environment
The code above expects a `dev-flags-overrides.js` file in every environment.

Obviously, you can add it manually to each local environment and instruct all the developers to do so via documentation.

However, we'll have to think about how this will work when the app is deployed to various environments (i.e. staging and production).

In the deployment pipeline, you'll need a step for adding the `dev-flags-overrides.js` file with the overrides appropriate to the environment.

For production, you can create a `dev-flags-overrides.js` file that returns an empty object.

For staging, you can copy the defaults and enable them as needed.

## Consuming Dev Flags
Once you have the dev flags patterns set up per environment, you can start writing code that toggles features based on a flag.

Since dev flags frequently control revealing features of an in-progress project, you'll want to make the `!devFlags.someFlag` code easy to cleanup (since it will eventually go away:

```jsx
// Home.jsx

import devFlags from './dev-flags.js';

// Bad
function Home() {
  if (devFlags.someFlag) {
     return ...;
  }

  return ...;
}

// Good
function Home() {
  if (!devFlags.someFlag) {
     return ...;
  }

  return ...;
}
```

Then to release a feature, you can cleanup the dev flags by deleting the `!devFlags.flag` code (as opposed to copy and pasting the code in the `if` block in the bad example).

Sometimes, you may wish to enable a dev flag to release a feature/project as opposed to cleaning it up.

This may be nice so that you can quickly enable the feature, make sure everything is sound, and then delete the flag and all its references in a later commit.

🎉 Now you have an organized pattern for enabling/disabling features by environment. If you don't have something like this in your codebase, follow the steps above (it's just a few small files).