Feature Flags with Spring

I just announced the new Spring 5 modules in REST With Spring:

1. Overview

In this article, we’ll briefly define feature flags and propose an opinionated and pragmatic approach to implement them in Spring Boot applications. Then, we’ll dig into more sophisticated iterations taking advantage of different Spring Boot features.

We’ll discuss various scenarios that might require feature flagging and talk about possible solutions. We’ll do this using a Bitcoin Miner example application.

2. Feature Flags

Feature Flags – sometimes called feature toggles – are a mechanism that allows us to enable or disable specific functionality of our application without having to modify code or, ideally, redeploy our app.

Depending on the dynamics required by a given feature flag we might need to configure them globally, per app instance, or more granularly – perhaps per user or request.

As with many situations in Software Engineering, it’s important to try to use the most straightforward approach that tackles the problem at hand without adding unnecessary complexity.

Feature flags are a potent tool that, when used wisely, can bring reliability and stability to our system. However, when they’re misused or under-maintained, they can quickly become sources of complexity and headaches.

There are many scenarios where feature flags could come in handy:

Trunk-based development and nontrivial features

In trunk-based development, particularly when we want to keep integrating frequently, we might find ourselves not ready to release a certain piece of functionality. Feature flags can come in handy to enable us to keep releasing without making our changes available until complete.

Alternatively, we might need to use a different security configuration for non-production environments from that used in the production environment.

Hence, we could take advantage of feature flags to toggle the right setup in the right environment.

A/B testing

Releasing multiple solutions for the same problem and measuring the impact is a compelling technique that we could implement using feature flags.

Canary releasing

When deploying new features, we might decide to do it gradually, starting with a small group of users, and expanding its adoption as we validate the correctness of its behavior. Feature flags allow us to achieve this.

In the following sections, we’ll try to provide a practical approach to tackle the above-mentioned scenarios.

Let’s break down different strategies to feature flagging, starting with the simplest scenario to then move into a more granular and more complex setup.

3. Application-Level Feature Flags

If we need to tackle any of the first two use cases, application-level features flags are a simple way of getting things working.

A simple feature flag would typically involve a property and some configuration based on the value of that property.

3.1. Feature Flags Using Spring Profiles

In Spring we can take advantage of profiles. Conveniently, profiles enable us to configure certain beans selectively. With a few constructs around them, we can quickly create a simple and elegant solution for application-level feature flags.

The previous example builds on top of Spring Boot’s conditional configuration and configures one component or another, depending on whether the property is set to true or false (or omitted altogether).

The result is very similar to the one in 3.1, but now, we have our namespace. Having our namespace allows us to create meaningful YAML/properties files:

By putting our feature flags’ state in a cohesive unit, we open up new possibilities, allowing us to easily expose that information to other parts of our system, such as the UI, or to downstream systems.

3.4. Exposing Feature Configuration

Our Bitcoin mining system got a UI upgrade which is not entirely ready yet. For that reason, we decided to feature-flag it. We might have a single-page app using React, Angular, or Vue.

Regardless of the technology, we need to know what features are enabled so that we can render our page accordingly.

Let’s create a simple endpoint to serve our configuration so that our UI can query the backend when needed:

There might be more sophisticated ways of serving this information, such as creating custom actuator endpoints. But for the sake of this guide, a controller endpoint feels like good enough a solution.

3.5. Keeping the Camp Clean

Although it might sound obvious, once we’ve implemented our feature flags thoughtfully, it’s equally important to remain disciplined in getting rid of them once they’re no longer needed.

Feature flags for the first use case – trunk-based development and non-trivial features – are typically short-lived. This means that we’re going to need to make sure that our ConfigProperties, our Java configuration, and our YAML files stay clean and up-to-date.

4. More Granular Feature Flags

Sometimes we find ourselves in more complex scenarios. For A/B testing or canary releases, our previous approach is simply not enough.

To get feature flags at a more granular level, we may need to create our solution. This could involve customizing our user entity to include feature-specific information, or perhaps extending our web framework.

Polluting our users with feature flags might not be an appealing idea for everybody, however, and there are other solutions.

Gradual rollout: Flags enabled for a percentage of the user base. This is useful for Canary releases, for example, when we want to validate the behavior of our features

Release date: We could schedule flags to be enabled at a certain date and time. This might be useful for a product launch, a coordinated release, or offers and discounts

Client IP: Flagged features based on clients IPs. These might come in handy when applying the specific configuration to specific customers, given they have static IPs

Server IP: In this case, the IP of the server is used to determine whether a feature should be enabled or not. This might be useful for canary releases too, with a slightly different approach than the gradual rollout – like when we want to assess performance impact in our instances

ScriptEngine: We could enable feature flags based on arbitrary scripts. This is arguably the most flexible option

System Properties: We could set certain system properties to determine the state of a feature flag. This would be quite similar to what we achieved with our most straightforward approach

5. Summary

In this article, we had a chance to talk about feature flags. Additionally, we discussed how Spring could help us achieve some of this functionality without adding new libraries.

We started by defining how this pattern can help us with a few common use cases.

Next, we built a few simple solutions using Spring and Spring Boot out-of-the-box tools. With that, we came up with a simple yet powerful feature flagging construct.

Down below, we compared a couple of alternatives. Moving from the simpler and less flexible solution to a more sophisticated, although more complex, pattern.

Finally, we briefly provided a few guidelines to build more robust solutions. This is useful when we need a higher degree of granularity.