Fantastic Features and How to Hide Them

Fantastic Features and How to Hide Them

Symphony Logo
Symphony
January 27th, 2022

Imagine a scenario. You are working on a new complex feature. Your team releases to production every two weeks, but to complete the feature, you need more than that.

You can always create a feature branch and postpone merging to the main branch until everything is done, but this brings some risks. You need to make sure your changes are in sync with the main branch. This requires delicate planning and putting those plans in action promptly. You should know what can happen if this is not the case. Many of us probably had painful experiences of merging long-lived branches in the past.

There is another approach you can use: a technique called feature flags.

What are feature flags?

Feature flag (feature toggle, feature switch, feature gate, etc.) is a technique in software development that makes it possible to change system behavior without changing the code. Meaning, it is used to show or hide a feature during runtime. This is not always the case but we’ll get to that later.

In the simplest terms possible, it is just a switch that enables or disables a feature within an application or a system. If the switch is “on”, users will have access to the feature, and vice versa.

The mechanism can be implemented in-house, but there is a myriad of full-blown solutions that provide support for most mainstream programming languages.

Some of the solutions out there include:

  • Unleash - supports multiple programming languages (both frontend and backend) and has Docker support for proxy and server services. Provides a free plan with the open-source license as well as paid plans with a lot of additional features.
  • Statsig - also supports multiple programming languages, with no official Docker support. Has a free developer license available as well as paid plans with additional features.
  • LaunchDarkly - same as the previous ones, a lot of SDKs and Docker support. There is no free plan.

The why

So why do I need feature flags, you ask? If you’ve ever needed to validate a new functionality with end-users (test code in production!), quickly roll back a change, or run an A/B test on a feature, there’s your answer.

Feature flags go well with the principles of agile development, where continuous delivery and continuous integration (CI/CD) play a big role in providing development teams with rapid feedback about their code. If you want continuous delivery, backed with reduced deployment risks, feature flags are the tool for you.

add-some-feature-flags-2.webp

Adding switches to your code

A feature flag in a code is essentially an IF statement. Adding a feature flag could be as simple as defining a local variable that controls rendering a feature. 

var enableNewAwesomeFeature = false;

 

if (enableNewAwesomeFeature) {

 renderNewAwesomeFeature();

}

Sometimes we are working on improving the existing algorithm and want to control which one is being used. We will use this example and see how we can (and probably should) evolve the local variable to the actual feature flag that gives the option of toggling a feature in a matter of seconds.

 Before:

function myCoolFeature(){

 // current implementation lives here

}

After:

function myCoolFeature(){

 var useNewAlgorithm = false;

 // useNewAlgorithm = true; // UNCOMMENT IF YOU ARE WORKING ON THE NEW ALGORITHM

 

 if (useNewAlgorithm) {

   return enhancedAlgorithm();

 } else {

   return oldAlgorithm();

 }

}

 

function oldAlgorithm(){

 // current implementation lives here

}

 

function enhancedAlgorithm(){

 // TODO: implement better algorithm

}

This is the simplest way to add feature flags. The benefits of this approach are that it’s pretty simple to implement, the control of the flag is brought down to the mere change of the variable value, but it has its drawbacks. To change the flag, you need to change the code itself and redeploy the application for changes to take place.

Let’s make this a bit more dynamic.  

function myCoolFeature(){

 if (featureIsEnabled("use-new-algorithm")) {

   return enhancedAlgorithm();

 } else {

   return oldAlgorithm();

 }

}

By creating a new function that checks the value of a specific toggle, we’ve extracted the logic from the code itself and made it possible to dynamically control which path should be used.

There are many ways to implement how this check works. It can vary from a simple in-memory store, properties in a configuration file, or row entries in a database, to a sophisticated system with a fancy user interface. Now that we have everything in place, let’s see how this would help us switch from one algorithm to another without the need to redeploy our code. For the simplicity of this example, let’s say we read the feature flag configuration from the database, and it is a simple toggle without any custom rules.

With the feature flag in place, we continue the development of our new algorithm and, using CI/CD, have all the changes deployed to production as soon as they are completed. If we want to release this new algorithm for our users, we can simply change our toggle in the database and all users can use it without the need for redeployment. Also, if we encounter an issue, we can switch back to the old algorithm in a matter of seconds.

In case we need a more granular configuration or our feature flag, we can use different strategies and choose those that best fit our needs.

Strategies

When it comes to ways of how feature flags are applied, there are a couple of strategies that can be taken into consideration:

  • Standard feature toggle - gives the option for turning a feature on/off.
  • Environment-based - turn a feature on/off for a specific environment (e.g. staging, all pre-production, etc.).
  • User-based - turn a feature on/off for some users.
  • IP address - turn a feature on/off for specific IP addresses.
  • Request based - turn a feature on/off for each request (or use a more complex decision algorithm).

This list can be extended to the needs of your system. If those exist in your system, you could decide to toggle a feature based on a user group; or maybe you have a way of assigning some metadata to users (e.g. tagging) and using that to form a strategy for a specific feature flag.

Feature flags can also be used to support canary releases.

A canary release enables the rollout of features and we can have them tested by a small percentage of users. Feature flags allow targeting users by one of the criteria mentioned above, and if a feature performance is not satisfactory it can be turned off without any undesired harmful effects.

Costs

Using feature flags requires additional planning and coordination, as well as a change in how we work. Apart from that, there are some other additional costs that we need to consider when using feature flags extensively.

The first one is the runtime cost. Depending on the way we store feature flag metadata, it can take some time to fetch and load the information about the flag to perform a check (network latency, querying the database, etc., can reduce the responsiveness of our application significantly). That’s why it is important to figure out how to reduce this cost most effectively (adding caching mechanisms and keeping them up-to-date).

Another cost that comes with feature flags is technical debt. After a flag has been fully enabled it leaves a lot of dead code that needs to be deleted. 

dead-code-dead-code-everywhere-2.webp

It usually needs to be done manually after some period from feature rollout has passed (a few days or weeks) but some organizations managed to automate this process to some extent.

Removing obsolete flags

Sometimes it is hard to prioritize managing feature flags and the ownership over them is not always completely clear. You could “set” an expiration date on a flag but the team can be swamped with the development of new features and/or improvements to the existing ones. And since it looks like this dead code does not hurt anyone, it can be kept low on the list of priorities.

Keeping obsolete flags can result in exposing end users to the wrong feature or a breaking functionality. That’s why it’s important to remove them as soon as possible.

There are a couple of approaches to mitigate the problem of obsolete flags. Sometimes it is clear that a feature flag can be removed and it’s as simple as creating a new ticket and getting it done in parallel with the rest of the development, but other times it needs more thought put into it.

That is why some teams have special ceremonies related to removing obsolete feature flags. It can either be a day of the month when the whole team is dedicated to removing outdated flags or an event where the team could only analyze, and prepare tasks for removing them. 

Conclusion

Feature flags are a powerful technique that attempts to provide an alternative to maintaining multiple branches in source code, such that a software feature can be tested even before it is completed and ready for release. This aligns perfectly with the principles of agile development and CI/CD. It enables developers to roll out their features to production rapidly and safely, and with better control.

On the other hand, feature flags come with some costs that need to be addressed correctly if we want to avoid cluttering of technical debt and impact on the performance of the application.

If used correctly, they can help teams ship code faster, reduce deployment risks, and make development and code reviews of new features easier. Overall, the benefits highly exceed the costs that we mentioned.

now-you-know-ff-2.webp

Source for all images: https://makeameme.org/, https://imgflip.com/, and https://memegenerator.net/.

About the author

Milivoj Kovacevic is a full-stack software developer with over 7.5 years of experience as a professional software engineer working at our Novi Sad engineering hub. Meet Milivoj here.

Milivoj is a hardworking and passionate developer whose dedication and goal-oriented work style are a tremendous benefit for the team he is a part of. In his team lead role, he has been leading a number of software developers so he had the opportunity to develop essential mentorship skills. Recently, Milivoj worked mostly on Java, Spring Boot, micro-services, and React engagements. His main interests are backend-oriented technologies, following software development best practices and principles, implementing design patterns, and focusing on delivering quality.

Contact us if you have any questions about our company or products.

We will try to provide an answer within a few days.