Lesson 1: What are feature flags?
A feature flag is a configuration tool that gives the application owner the ability to remotely control different ways in which a specific part of the application should behave-without having to change or deploy new code. A feature flag configures each running application according to certain criteria, commonly referred to as 'targeting criteria'. Examples of targeting criteria are: country where the application is running from, version of the application, etc.
The following video provides a quick overview of what feature flags are and how they can be used:
In the common scenario, a feature flag is functionality you add to your code that lets you control the behavior of some part of your application remotely, without changing the code. Your application asks a feature flag resolver for what behavior a certain user should have given a certain context. This is called "resolving" the feature flag.
Feature flags get much more powerful when paired with an experimentation system and/or a user-behavior instrumentation system. In this case, you can use the feature flag to run randomized experiments that let you evaluate the impact of changing from one behavior to another. This is often referred to as A/B testing. In this case, different users are given different 'treatments' by receiving different flag values.
Use cases for feature flags
Feature flags can be used to:
- Toggle features on and off for certain users.
- Toggle features on for the product development team to QA new features.
- Serve different experiences to different users, and evaluate the most successful experience according to the underlying experimentation system.
- Try out a new design of your UI elements.
- Gradually roll out a new feature and control who gets the feature.
- Adhere to policies that can change over time, for example what people in a specific country can/cannot see.
Things to be mindful about
When using feature flags that are controlled by an external system there are a few things to be mindful about. Thinking about these things early in your feature flag journey can save you a lot of time and headaches later on.
Default values
It's always important to consider the default scenario so your app continues to function even though you cannot resolve a specific flag, or if the resolve returns no value for the flag.
If a feature flag changes/updates in such a way that your client cannot deal with it you need fallback solutions for that. A typical case for when this can happen is when you have a mobile application where you cannot control when that app is updated on the client side, or if the network call to the feature flag resolver fails or times out. Most feature flag providers have default values defined in the code that help handle these cases.
Falling back in default values is not a problem, rather it's a good practice, you just need to be aware of it. If you are adding a new feature flag, you set the default value to the current hard-coded behavior that the static code implied. This way, you can merge the feature flag into your codebase without worrying about changing the behavior of the app.
Flicker
Flicker is a phenomenon where the value of a feature flag is changed rapidly in a way that is noticeable to the user and gives a flickering experience. For example, a button might appear and disappear rapidly.
Technically, what often happens is that the code in the app is changed because the feature flag controlling the code is updated in front of the eyes of the user.
This can happen if a feature flag is not resolved quickly enough (high latency) or if the feature flag is implemented in the code in a way that doesn't suit the use case.
When the context of the user changes, it might be expected that the feature flag value changes. Whether this is a problem depends on the use case. See Lesson 4 for more information.
There are many ways that flickering can be avoided. You should ensure that the feature flag provider has latency that is acceptable to you. Beside the latency aspect, you need to ensure that the implementation of the feature flag in the code is correct, and follows the recommended patterns.
Confidence provides feature flag resolving in three ways to help minimize latency:
- Online resolving: the standard way, making a network call to the Confidence feature flag resolver
- Sidecar resolver: set up the Confidence resolver as a sidecar in Kubernetes, reducing latency to that of a local call
- Edge resolving: use an edge compute provider like Cloudflare or Fastly to resolve the feature flag on the edge, by running the Confidence resolver in an edge worker
Complexity levels
A feature flag can be increasingly complex. From a simple boolean on/off flag, to a 'Super Flags' that contain a lot of configuration information in a JSON-type object that can be nested. There is no right or wrong answer here and it depends very much on your situation and needs.
If you are interested in optimizing a certain user experience with A/B testing,
for example the title string of a page, you could use a flag with properties
like title, color, and font-size. Where title and color are of type
string, and font-size is of type integer. With this setup, you can compose
different experiences by changing the title, color, and font-size of the page,
without having to change the code. This enables non-engineers to iterate on an
experience without having to rely on the engineering team.
Connecting various aspects of a user experience to a feature flag with several properties is called 'parametrizing' the user experience. You add parameters to certain aspects of the user experience, and then control these parameters remotely via the feature flag.
Fetch and resolve flags
When your application asks the feature flag resolver for the value of a flag, there are in fact two steps. The first is fetching the flag which means getting the correct flag value for a given user in a given context from the API. The second step is resolving the flag which means applying or using the flag value in the app.
The separation between fetching and resolving is important because it allows you to control when you want to get the flag value and when you want to apply it.
Common patterns are to fetch the flags in the background, during the session, or at startup with a loading screen.
In order to make a decision whether you want to fetch your flags in the background, in the session or in a loading screen, think about what types of experiment you want to conduct. If your experiments are targeted on UI changes, it's advised to fetch and resolve as soon as possible and in bulk - this way, you can guarantee no flickering and all in all good user experience. If you're experimenting with platform, backend, etc - you can get away with showing your app without loading screen and fetching the flags in the background thread without compromising user experience.