Feature flags in a Single Page Application: a cookbook


About feature flags

Feature flags are awesome: they allow you to enable/disable one or many features in your application at runtime. Why is it important? Because it allows to release many flavours of an application with a single codebase.

Maybe it doesn't sound much, but here's the gist: Your end-users and your QA team will deal with the same codebase, but your QA can test this brand new feature you're working on while end-users do not even notice.

Among the many benefits are:

  • prevents us from doing branch-based deploys
  • allows to test the new features against the most recent codebase
  • allows to ship a new feature bit by bit
  • we only deal with the master branch: no more hotfix on many branches, no more backports, etc.
  • we deploy the same codebase to every environment: no more staging, QA, etc. Everything is handled by feature flags. That's pure bliss, if you ask me.

We've learnt a lot while refining our feature flags strategy, and this post will share the 3 major feature flags strategies we use daily while developing our frontend applications.

The many feature flags

1. The explicit map

The simplest way to define a set of feature flags is to set, somewhere in the configuration of the app, a map of all the available feature flags with their default value:

const features = {
  withLoginScreenWithSocial: false,
  withServiceWorker: false
}

How your app takes into account this map is left to your implementation, but do not forget that as presented here, they are static, which means they can not be changed at runtime.

Allowing to update the feature flag map

It is crucial to offer some way to update this map. In our case, we do as follow:

  • The map is frozen in production environment: it can not be altered by the end users, the set of enabled features is under control.
  • The application has a config page (except in production) that allows to toggle every feature declared in the map. This page itself is enabled on every environment except in production and allows for the (internal) users to try the new features before they are released.

Enabling is manual

The upside of this type of feature flag is that you keep all the control over the status of the feature flag.

The corollary downside is that once it has been approved, you have to manually change the default value to enable it by default, the lifecycle management of this kind of feature flag is more tedious (yet more controlled, let's not forget that) than with the 2 other strategies below.

2. Data-driven feature

For the design pattern lovers, this one could be named "Inversion Of Control" for features.

The idea is simple: Depending on your expectations on the response of an API call, enable or disable a feature.

Here's a simple example: Let's assume that the description of the current user is accessible by performing a GET /accounts/me request. Let's assume that a new feature involves the date of birth of the current user, but alas, it is not available currently.

Here's your feature flag, in a nutshell: If the response payload contains a birth_date property, activate the feature flag withBirthDate. In other (javascript-y) words:

if (payload.hasOwnProperty("birth_date")) {
  features.withBirthDate = true;
}

The main advantage of this type of feature flag is that as you don't get to decide when to enable/disable the feature flag, the feature is automatically in sync with the API (or whatever tier sends the data). Feature flags are rarely frontend-only, so with this one type, not having to sync both ends of the network really helps.

3. Environment-driven feature flag

This last type we try to avoid but it comes in handy from time to time. Here's the gist: We know on which environment we are running based on the current domain; myAppName.local is a development environment, for instance.

So, there are some features that we only enable in some environment. We tend not to do too much because we want to force ourselves into thinking that, from our perspective, an environment is not a solid metric to know what to enable or disable.

It's good to know we can rely on it, but we prefer to be either completely explicit (with a feature map) or data-driven.

Conclusion

There's a lot to be added to cover extended uses of feature flags, such as A/B testing, cohorts, fine-grained activation, lifecycle of a feature flag, etc. but you now have a solid overview of what you can achieve by solely working on your frontend codebase.

Feature flags are of immense help in releasing better software and they are the kind of tools we really like: simple to understand and simple to use.

There is room for more details and more posts will come, but if you can't wait and you want to build frontend applications in a stress-less, fast-paced environment, we're hiring!