Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Feature flags: why, how, all that

(I'm enjoying doing these raw, barely edited writeups; I hope they're useful to you too)

Feature flags

This is my own writeup on feature flags; for a deep dive I'd recommend something like Martin Fowler's article (https://martinfowler.com/articles/feature-toggles.html).

So. Feature flags. The basic idea that you'll store configuration/values on a database/service somewhere, and by changing those values, you can change the user experience/features for a user on the fly.

Let's say that you're building a new feature, called 'new-button' which changes the color of buttons, which is currently red, to blue. Then you'd change code that looks like this -

function Button(props) {
  return <button style={{ backgroundColor: "red" }} {...props} />;
}

to this -

function Button(props) {
  const isNewButton = useFlag("new-button");
  return (
    <button
      style={{ backgroundColor: isNewButton ? "blue" : "red" }}
      {...props}
    />
  );
}

(NB: This is a completely made-up implementation, I used a hook here just because.)

This is as simple as it gets. The value of isNewButton is fetched from a service somewhere, usually just once for the lifetime of the application (possibly right when the application starts?) Someone could then go to a hosted page somewhere listing all the configuration values, flip a switch, and decided whether to turn the button color red/blue. Flags don't have to be just booleans; they could be strings, or numbers, or any other values as well.

But wait. Some of this sounds like over-engineering. Why not simply have a module (say, features.js) that exports an object with these values? You could change them whenever, commit and deploy, and that would achieve the same thing. Or if you're going to use a service, why not a regular configuration service? Or a regular key value store with a simple UI on top of it?

The answer is targeting.

Feature flag services let you serve different values for different groups of people (based on whatever conditions you choose.) Typically, they'll integrate with some form of user service that you're using in your application/firm, and you'll be able to target values based on identity (like a user id), some grouping condition (like, what teams/groups a user belongs to), or a bucketing system (e.g - only for x% of users, where x can be dynamically changed at any time). This is an incredibly powerful feature that lets you achieve many things:

  • mainline development: All PRs directly target your main branch. All new features are wrapped in a feature flag, and are turned on and off only once in a deployed environment. This also means you only need to deploy in-progress work to one environment/server, instead of needing different targets for different PRs, for example.

  • prototypes: Want to build a quick product idea and share it with (only some) people? Put it behind a feature flag, add those people to the flag's filter, done.

  • demos for stake holders: similarly, if you're building some cool stuff and want to impress your boss, or their boss, or some new client; feature flags ftw.

  • qa on prod: Instead of deploying new changes to another environment for QA (realistically, a farm of machines, for every new feature being developed), the application can be deployed to production directly, turning on features for QA/UAT folks either manually by IDs, or targeting their team as a group. Once they signoff on features, it can be turned for users without any new deploys or releases.

  • gradual rollouts: You can mark feature flags are being turned on/to a particular value for a given percentage of users. This lets you roll out the feature gradually; you can observe analytics to make sure nothing is broken and you're seeing expected results (based on what the feature achieves), and if all is going well, roll it out to more people. Similarly, if you see something wrong, or start getting bug reports about something being broken, you can turn it all the way down without a fresh deploy, which gives you time to investigate and fix the problem. Super neat.

  • (Y'all got any more ideas? Lemme know.)

Notes

  • You want to be sure that your application is resilient to the feature flag service failing; i.e. make sure the 'default' choice is the safe choice.

  • You may get concerned that your codebase will end up as a tangle of if-conditions, littered with feature flags. This is a good problem to have; a better problem than not being able to ship features, getting mired by infrastructure/architecture/process problems! I would recommend doing a regular cleanup; you could possibly run a script on your codebase every month or so, and detect flags that are set at 100% in production. Your script could get smart and codemod out the branch as well.

  • A concern is that you'll end up shipping more code to a user than is generally used/required for the application. This is valid, and as such, feature flags for front end development are probably best for applications where bundles size isn't the most critical metric. Suggestions to improve this could be using dynamic imports for features (i.e. - condn? import('feature1') : import('feature2')), to integrating your build system with your feature flag service, generating multiple bundles based on combinations of feature flag values, and serving the right combination of bundles to your users (this is what big corps like fb do.)

  • How do you write tests for stuff behind feature flags? You would mock the feature flag service itself, and set the required flags to the values you need for the tests you're running. Nice.

  • Some examples of third party products (I haven't used any of these myself): LaunchDarkly, Optimizely, ConfigCat, split

  • Some good counterpoints here https://software.rajivprab.com/2019/12/19/when-feature-flags-do-and-dont-make-sense/

(todo: write about how to query values inline, but fetch all values at the beginning).

@sw-yx

This comment has been minimized.

Copy link

@sw-yx sw-yx commented Aug 8, 2020

"todo: write about how to query values inline, but fetch all values at the beginning"

feel like "suspense for everything" is a really nice pattern to have!

@threepointone

This comment has been minimized.

Copy link
Owner Author

@threepointone threepointone commented Aug 8, 2020

@sw-yx I have an idea here to do it without suspense. Consider -

  • enforce that the value passed to useFlag() is always a string literal so it can be statically analyzed.
  • run a script on the codebase and collect all the arguments passed to useFlag
  • for every main request to an html page, fetch all these values and inline as a global js object

useFlag will then always be sync, and values always available. It doesn't even have to be a hook, can be a regular function call anywhere.

@sw-yx

This comment has been minimized.

Copy link

@sw-yx sw-yx commented Aug 8, 2020

totally. i think there is some unnamed pattern in our future where it will be normal to statically extract all sorts of metadata from our code to run it upfront. with typescript we extract types. with gatsby and relay we extract graphql. with purgecss and babel we extract css.

why not more things? should all these be separate tools or should they be 1 tool with just a bunch of callbacks people can hook in to? why run this extraction/analysis/validation only at build time - why not always have it on? this is why i think the future is a generic "language server" for frontend dev that handles all this.

@NMinhNguyen

This comment has been minimized.

Copy link

@NMinhNguyen NMinhNguyen commented Aug 9, 2020

A concern is that you'll end up shipping more code to a user than is generally used/required for the application. This is valid, and as such, feature flags for front end development are probably best for applications where bundles size isn't the most critical metric. Suggestions to improve this could be using dynamic imports for features (i.e. - condn? import('feature1') : import('feature2')), to integrating your build system with your feature flag service, generating multiple bundles based on combinations of feature flag values, and serving the right combination of bundles to your users (this is what big corps like fb do.)

Is creating different bundles (like React bundles with its buildtime feature flags) noticeably better than dynamic imports? Roughly what is required to implement this serving of different bundles? SSR?

@threepointone

This comment has been minimized.

Copy link
Owner Author

@threepointone threepointone commented Aug 9, 2020

@NMinhNguyen there's a major difference between creating different bundles like React does, vs what facebook does with application code. React's bundles are based on predefined channels (defined in https://github.com/facebook/react/blob/master/scripts/rollup/bundles.js). Of note, this does not account for every variation/combination of feature flags, just the ones explicitly defined in ReactFeatureFlags.* in https://github.com/facebook/react/tree/master/packages/shared/forks. What I was referring to was having bundles available for every possible combination of feature flags (which would be a loooot more). It requires your bundler to be able to smartly generate chunks for these combinations, and then include them on your page based on the user visiting. There aren't any publicly available tools that do this, because it usually requires deep integration with your serving infrastructure, feature flag and identity services, etc.

@threepointone

This comment has been minimized.

Copy link
Owner Author

@threepointone threepointone commented Aug 9, 2020

@NMinhNguyen That said, it's not as big a concern if bundle size isn't you primary performance metric. It's fine to ship some extra code as long as you're diligent about cleaning up, and don't let it inflate beyond some critical number (like, don't accidentally send MBs of code)

@NMinhNguyen

This comment has been minimized.

Copy link

@NMinhNguyen NMinhNguyen commented Aug 9, 2020

Oh right, I just assumed that at Facebook, a lot more bundles of React were generated (re-built), e.g. various combinations of https://github.com/facebook/react/blob/0cd9a6de557f44dcfe79a4f287ca04b05d674bdc/packages/shared/ReactFeatureFlags.js#L44-L45 or any other new experimental feature.

@vacom

This comment has been minimized.

Copy link

@vacom vacom commented Aug 9, 2020

Hey I am the creator of https://www.upstamps.com

UpStamps is a Feature Flag Management Platform that lets you manage every feature with a central visibility and control user interface. Teams and developers are managing their projects using feature management to progressively deliver features to users with confidence.

Key Features:

  • 📄 Create Projects
  • 🌳 Organize by Environments
  • 🧪 Run A/B Tests
  • 🏳 Deploy with Feature & Remote Flags
  • 🎯 Target Specific Segments

Nice Gist, UpStamps uses a lot of the pattern in your post. Support with React Hooks

@dreamer01

This comment has been minimized.

Copy link

@dreamer01 dreamer01 commented Aug 9, 2020

Worked on something similar where I had to show/hide eye icon in secret fields depending on the flag for each deployment.
But I used an object configuration for that, which was requested from the BE service during project initializing phase.

Your approach would be helpful, when we decide to move control to client side also.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.