At Builder.io we are huge fans of utilizing feature flags to make our development and deployment process run smoothly. They help us to ship products faster, update feature availability without deploys, and minimize risk, all while keeping the engineering team smiling. It seems like as each week goes by we find another perfect use case for feature flags, but in this blog post I want to outline a few different ways we use feature flags.
First things first, what is a feature flag? Feature flags are like little switches that live in your code and allow you to modify behavior or features without modifying the source code or requiring a redeploy. The most common form is a simple boolean value, but they can be numbers, strings, JSON objects, or pretty much any other value available to you in the language you are using. Although it is a simple concept at the surface level, details like managing fine-grained access at the user level, percentage-based rollouts, and other nuances can quickly add complication if you are trying to develop a feature flag system of your own. Because of this, at Builder we chose to use Launch Darkly to manage our feature flags.
One way we like to use feature flags at Builder is to get unfinished features merged into the main branch as soon as possible. The usual development process for a new feature might involve branching off of main, making all your changes, then sending a PR back to main once the feature is all complete. The problem with this approach is that if the feature takes more than a few weeks to complete, you might run into all kinds of merge conflicts when it is time to merge. Plus, other team members might have added adjacent functionality that needs to be reconciled with your feature, which leads to headaches for everyone and a delay in the launch.
The alternative approach we like to take is to wrap the new feature code in a boolean flag that can be toggled independent of deploys, and merge it in as soon as possible. That way we can develop and merge often without the fear of merge conflicts. An example of this can be as simple as:
if (features.has('enable-cool-new-feature')) {
// execute your feature specific code
}
Everyone likes to believe their code and application is bullet-proof. We at Builder try our best to keep our platform stable and healthy, but sh*t happens and sometimes we break functionality. When your app breaks, the most important task is to fix the issue, but almost as important is letting your customers know that you are aware of the issue and working hard to fix it.
For example, we had a service disruption a few months ago that caused one of our main in-app pages to show an endless loading spinner. It was not a great user experience and was super confusing to our customers to get thrown into an endless loading spinner page without any explanation or error message. We came up with the idea of being able to pop up an in-app error message to notify users of any system-wide issues we might experience, and have that be toggled via a UI somewhere so we would not have to detect any special error states or redeploy.
In order to have maximum flexibility with the messaging, we decided to bend and stretch our feature flag system to toggle on the error message as well as set the message details. You might be saying, “isn’t Builder a visual cms? Why not just use your own product instead of bending Launch Darkly to act as a weird fake CMS?!” We figured that if we are displaying this error message because Builder itself is having an issue, we don’t want to get caught in the situation where Builder is busted so we need to show an error, but since Builder is busted we can’t flip the toggle to show the error in-app! 😅
To do this, we first created a JSON type feature flag in Launch Darkly that has a few properties we can use in our code:
Then in our code we wrote a component that shows the message if we activate the flag:
// Simplified message component
export const ServiceDisruptionMessage = () => {
const flagInfo = featureFlags['service-disruption-manual-message'];
return (
<>
<div>{flagInfo.title}</div>
<div>{flagInfo.message}</div>
<Button href={flagInfo.linkUrl}>
More info
</Button>
</>
);
});
Voilà! Then whenever we need to — which is hopefully never — we can click the button in Launch Darkly to display the message and notify our customers we are aware of the issue and taking care of it as fast as we can.
Integrating feature flags into our development flow lets us do lots of things, but rolling out new features or code changes in a percentage-based manner is one of our most common strategies. This can be a great approach that allows you to slowly introduce new functionality and roll back quickly if you hear from customers that something is broken.
For example, we recently needed to update the Stripe API version our backend is using. We read all the relevant Stripe changelogs and docs, tested the heck out of the change, and did everything we could to be certain the change was not going to cause problems for our customers. However, there is still the possibility that some obscure change or something we somehow missed will cause issues for our users. So instead of making the change and shipping it out to 100% of our customers, we choose to slowly roll it out in a percentage-based manner using a string type feature flag. With a feature flag system it becomes quite easy to do. In your code you can write something like this:
const stripeApiVersion = await flags.variation(Features.features.StripeApiVersion);
// use stripe api version version somewhere else in the code
await axios.get(`${stripeUrlBase}/{stripeApiVersion}`)
The in the UI of the flag system it looks like this:
Bam! Now we can quickly update and modify the rollout based on user feedback and errors we see in the logs.
Another super nice perk of using feature flags is the ability to target specific users for targeted feature release or functionality changes. The way it works is that when the feature flag service is initialized, we pass in all the relevant user information so that the feature flag service can determine what variation to show.
For example, we can build out a feature and then roll it out to a few specific beta testers in order to get lots of feedback before releasing a potential feature to all of our customers. This allows us to iterate fast and ship quickly without risking breaking a core functionality for any of our customers.
Whether you decide to try and hack your own system, host an open source solution, or use a hosted third-party solution like Launch Darkly, feature flags are fantastic friends. At Builder we have found them to be an indispensable tool to ship value to our customers as fast as possible.
Introducing Visual Copilot: convert Figma designs to high quality code in a single click.