Fun with Flags I

A Brief Introduction to Feature Flags

A simple yet powerful concept that has probably existed since the dawn of software, gaining strength with the introduction of Continuous Delivery and A/B Testing in the last decade. Configurable “code paths” allow for easy switching between versions of our program, enabling two (or more) alternative functionalities to coexist, which can be selected simply by modifying a parameter located elsewhere in the system. This parameter can be part of the code, managed as an environment variable during deployment, stored in a database and accessible from a configuration interface, or even reside in an external service (either within the system itself or provided by a third party).

I’m not going to reinvent the wheel by trying to explain in depth something that can be found in (at least) two free books promoted by leading platforms in the management of feature flags. I have read them, and they have guided me in working with them over the past few years. I invite you to download and read them.

Effective Feature Management (John Kodumal) https://go.launchdarkly.com/rs/850-KKH-319/images/launchdarkly-oreilly-effective-feature-management.pdf

Managing Feature Flags (Adil Aijaz & Patricio Echagüe) https://www.split.io/wp-content/uploads/oreilly-managing-feature-flags.pdf

What I will do next is briefly comment on some very important points that, in my experience, are worth considering when working with feature flags.

Complexity

One detail to keep in mind when starting to add feature flags to our system is the additional complexity. Each feature flag introduces a new dimension in the possible state space of the system. With two flags, for example, there are four possible combinations (enabled/enabled, enabled/disabled, disabled/enabled, and disabled/disabled). This space grows exponentially with the number of flags, as each one doubles the number of possible combinations.

These combinations are, of course, states of the system as a whole that will normally go unnoticed when we add our feature flag in a specific part that is simple to manage separately. However, when taken together, they can quickly become a challenge for the QA of the entire system. If we add multiple teams responsible for different flags in their domains, or worse yet, in shared domains where the outcome of the flags indirectly affects other parts of the system, we may find ourselves in a rather delicate scenario.

Therefore, it is very important to consider, before we begin adding feature flags, what the impact on increasing entropy will be. Although it may seem counterintuitive, since we are adding “control points,” we will disorder our system in a way that can easily slip out of our hands. Here are some points to consider to mitigate the negative effect that flags may have on our system:

  • Limit the Lifespan of Flags: Establish a clear plan to eliminate feature flags when they are no longer needed. Implement periodic reviews to identify and remove obsolete flags.
  • Document Purpose and Usage: Document each feature flag with its purpose, scope, and removal plan. Ensure that all team members know how and when to use each flag.
  • Avoid Nested or Dependent Flags: Minimize dependencies between feature flags to reduce the complexity of interactions. Avoid nested conditionals that depend on multiple flags.
  • Implement Specific Tests for Active Flags: Create automated tests that cover relevant combinations of active feature flags to detect issues in common configurations.
  • Categorize Flags by Usage: Classify flags into types (experimental, access control, temporary configuration, etc.) and handle them according to their function. This facilitates management and removal.
  • Limit the Number of Simultaneously Active Flags: Do not allow too many flags to be active at the same time in the production environment. Implement policies to restrict the number of simultaneous flags.
  • Monitor the Impact of Flags: Use monitoring tools to evaluate the impact of flags on system performance and error frequency, which can help identify when a flag is more harmful than useful.

Maintenance

I have already discussed the importance of limiting the lifespan of flags. Each time we add one, we are adding a refactoring task to remove it in the future. It is crucial to schedule these tasks when introducing flags and to design the code in a way that makes it as straightforward as possible to eliminate them. Let’s remember the SOLID principle of open/closed (OCP): our classes dependent on feature flags should be closed for modification and open for extension.

To apply OCP when working with feature flags, you can follow these practices:

  • Use of Alternative Strategies or Implementations: Instead of adding conditionals directly in the code to check if a feature flag is enabled, create separate classes or modules that implement the alternative behavior. Then, use a design pattern like the Strategy Pattern to select the appropriate implementation at runtime based on the state of the feature flag. This way, you extend the behavior without modifying the original class.
  • Dependency Injection: Set up the system’s dependencies to be provided through dependency injection. When a new feature depends on the state of a feature flag, you can inject a different implementation of the functionality without modifying the existing code.
  • Use of Interfaces or Abstractions: Define interfaces that abstract the behavior. Instead of modifying concrete classes to support new features, add new implementations of the interface that are activated by the feature flag. This allows the system to be extensible without altering the existing code.
  • Centralized Behavior Configuration: Manage the logic of feature flags in a configuration layer or dedicated service. This way, the application classes do not need to know the details of the state of each flag, and the behavior can be extended without modifying individual classes.

When feature flags become unnecessary, they turn into technical debt, which increases the likelihood of errors, complicates code refactoring, and, as mentioned earlier, raises the risk of unexpected interactions. Additionally, a poorly documented flag poses a danger, as it could be reused or expanded in a way that deviates from its original intent, and this change could go unnoticed. Therefore, it is essential to follow a rigorous expiration strategy to preserve the integrity of the system.

Tips for Mitigating Technical Debt with an Expiration Strategy:

  • Set an Expiration Date for Each Flag: From its creation, define a specific date or condition under which the flag should be removed. This helps maintain a controlled lifecycle.
  • Actively Monitor Flags in Use: Implement tools or scripts to track the use of feature flags in the code and receive alerts when a flag has been active for too long.
  • Add Flags to Code Reviews: During reviews, check if feature flags are ready to be removed or if they are still necessary. Include flag review in the quality control process.
  • Document the Purpose and Retirement Plan of Each Flag in Detail: When introducing a flag, clearly document its purpose, who is responsible for it, and under what conditions it should be removed.
  • Automate Removal When Possible: If feasible, automate the removal of flags that meet the established conditions for retirement.

Strategy

It is important to understand what you want to achieve by using feature flags. Is it a tool to facilitate continuous deployment in our system, allowing us to decide when to make a feature release effective? Or is it a means to experiment with different versions before choosing one? Is it a safeguard method to quickly deactivate sensitive parts and mitigate disasters? This planning is part of a strategy, and it is essential to define it beforehand to properly classify and manage our flags.

Feature flags are deferred decisions, and if used deliberately as part of a plan, they can be a good idea. However, if we are uncertain about what we want to achieve or how to measure the impact of our decisions from the outset, it is better to clarify these points. What may seem like an advantage now could become a trap in the future.

In the previous points, we mentioned practices for defining a good strategy; however, it is important to emphasize the necessity of defining the objective of each feature flag. Before creating a flag, clearly establish its purpose and what is expected to be achieved with it, whether it is a gradual deployment, an experimentation phase, or a safeguard. It is also essential to set metrics to measure the impact of each flag; this involves defining key indicators that will allow you to evaluate if the flag is meeting its objective, such as performance, error rates, or user feedback.


When you have a hammer, everything looks like a nail. Feature flags are a very powerful hammer, so before filling your system with them, keep in mind that “with great power comes great responsibility,” and that a hammer can ruin your grandmother’s precious kitchen tiles.

https://launchdarkly.com/
https://www.split.io/
https://www.getunleash.io/
https://www.flagsmith.com/
https://configcat.com/


Posted

in

by

Tags:

Comments

Leave a Reply