Your Feature Flag Management Needs to Include Retirement

Written by: Erik Francis

7 min read

When evaluating software quality, feature flags offer a double-edged sword.  On the one hand, they can save you from technical debt.  But if you're not careful, they can also bury you in it. Technical debt, loosely speaking, is the idea that you let your software become suboptimal from a maintenance perspective in order to get to market quickly.  Like financial debt, this can be a valuable tool if you manage it well.  But it can also get away from you in a hurry if you're not careful. Feature flags get you to production more quickly and with less risk.  This means that you're typically going to have good deployment automation in place to accompany them and that you're also not likely to do horrible things to your codebase in order to hit a deadline.  And that's great. The downside, however, comes from the fact that feature flags are extra code in your codebase.

Introducing Cyclomatic Complexity

In the 1970s, a man named Thomas McCabe introduced a powerful concept to the world of software development: cyclomatic complexity.  The math and philosophy behind the concept are fairly sophisticated, but the basics are surprisingly easy to understand, even for non-technical folks.  Here's a simple example, using pseudo-code

public int add(int x, int y) {
  return x + y;
}

public int divide(int x, int y) {
  if(y == 0)
    return 0;
  else
    return x/y;
}

Here, we have two methods.  The first method, add, has cyclomatic complexity of 1, while the second method, divide, has cyclomatic complexity of 2.  The reasoning?  The first method has only one path through it.  You enter the method and return x+y.  But the second method, with cyclomatic complexity of 2, has two paths through it.  If y is zero, the method returns 0, but if not, the other branch executes and returns x/y. This relatively straightforward concept helps us measure codebase complexity, which, in turn, gives us a way to reason about technical debt.  Codebases with unusually high rates of cyclomatic complexity tend to have tech debt. And guess what feature flags do.  That's right.  They add additional paths through your codebase, thus increasing its complexity and maintenance difficulty.  Over time, as you add more and   more of them, the cyclomatic complexity keeps piling up, even if you stop using them.  Maybe you never pass 0 to the divide method above.  But either way, the complexity still exists. So, while feature flags help you develop a process for avoiding tech debt, they also add to it if you're not careful.  Let's look at how to prevent that.

Feature Flag Retirement

In the broadest sense, your solution is to take the advice in the title of the post.  You need a plan to send your feature flags to Florida to play golf and eat dinner at 4:30 PM;  you need to have a plan for feature flag retirement. In other words, your team needs to view feature flags as temporary residents of the codebase.  Through this lens, the feature flags truly are a form of technical debt, but they're the benign, useful form, like a mortgage.  You add them with eyes wide open, to fulfill a specific purpose, and with an end date in mind and they become a tool for you.  Feature flags have a lifecycle. When they've outlived their original purpose, you have a few types of retirement options.

  1. You can simply delete them.

  2. Replace them with a configurable, polymorphic scheme.

  3. Turn them into user-configurable settings.

Let's take a look at each of these options in a little more detail.

Retirement by Deletion

For a lot of implementations, feature flags have a very set window of usefulness.  For instance, take the case of a gradual rollout. You add some new functionality to your app, and you want to turn it on slowly, for only limited sets of users at first.  You'll show it to 1% of people and see if there are problems.  If not, you up it to 5%, 10%, etc.  Eventually, you'll show it to everyone. Once you've turned it on for everyone and it's live in production without issues, you're pretty near the end of this feature flag's lifecycle.  After some time passes where it's turned on for 100% of people in production, it moves from "contingent feature" to "just part of the software."  And when that happens, you don't want a vestigial flag that allows you to turn it off. So in this case, you want a plan to remove the flag in your next roll to production.  All you need to do is delete it from your code and from your feature flag management scheme.

Retirement by Admin-Configuration

Deletion is the conceptually simplest option, but it's not your only option.  You might need to retain the option granted by the feature flag indefinitely.  To understand what I mean, let's consider another common use case: a database migration. Say you have a web app that's making use of an antiquated database technology.  You want to modernize and move away from something outdated.  When the time comes to make this happen in production, you've already done some trial migrations to a sandbox and you have the new database setup.  So you perform the real migration, and have new and old databases running in parallel for a time in order to hedge your best.  You manage this with a feature toggle. Unlike a gradual rollout, you might want to keep the old database around for an extended period of time.  Maybe it's a "just in case" kind of thing, or, maybe, not all data is going to make the trip and you're going to need a "read only" window into the legacy database for historical reporting. In a situation like this, you're going to want to turn the feature flag into a configurable scheme, probably making use of software constructs like interfaces, rather than simple booleans.  So, assuming you started with a feature flag, you'll want to 'graduate' to this interface scheme.

Retirement by User Configuration

Lastly, consider a similar option, but one that's more user-facing than something like a database migration.  In this situation, you might retire a feature flag by making a user-configurable setting. To understand what I mean here, let's keep the example as simple as possible.  Say you want to run some A/B testing to see which color of "buy" button on your e-commerce app seems to drive more sales.  You can use feature flags to execute this experiment.  You'll show half of your users a green button and the other half a red button, and see which gets you more clicks. Frequently, you'll arrive at a decision based on the experiment results and then just delete the feature flag.  But maybe you get some feedback that users are happiest not when they get a certain color of button, but when they control the color of the button.  So you decide to make this a user-configurable setting. When you do this, you'll move the management of the conditional logic out of your feature flag management system and into a database or a file a user data.  It becomes a configuration setting in your application.

Use a Feature Flag Management System to Keep Track

This is not an exhaustive list of what fates may await your feature flags.  You'll probably retire the majority of them by deleting them, but some will stick around as settings for the ops group or for your users.  Others may work their way into the permanent codebase in other, unique ways. But you need to make sure that none of them just sit around adding complexity as dead code.  The particulars of the retirement don't matter as much as the fact of the retirement. And, without a sophisticated feature flag management strategy, you're going to have a really hard time keeping track of all of the feature flags you have and where in the lifecycle they are.  Feature flags give you a lot of important capabilities, but they put you at risk of runaway tech debt.  Feature flag management systems let you reap those same benefits while mitigating the risk of tech debt.  And part of that mitigation is the ability to track, manage, and, yes, retire your feature flags.

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.