Feature toggle in Spring Boot 2

Whether you like it or not, software development is a collaborative activity. Integration work has always been demonized and treated as necessary evil. There are several approaches which try to solve the challenge of effective integration. The feature toggle belongs to that group. In this article, you’ll see in practice how feature toggles, also known as feature flags, can be used in your Spring Boot application.

Advertisement

1. What is feature toggle?

Simply put, Feature toggles are variables which allow execution of alternative paths in an application based on their current values. By keeping different scenarios of execution, you can modify the behavior of the application without altering the code.

Depending on your needs, toggles’ values can be set before the startup of your application or adjusted at runtime. In the latter case, changes of a value can be persisted or affect only the current execution of the application.

Usually, you read about feature flags as an alternative for feature source code branching, however, in practice both techniques can be used together. For instance, you can use feature branches for development of new user stories in the application, while feature toggles can be applied to control access to features on separate environments (e.g. clients with different  requirements).

Despite many uses, feature toggles have also their drawbacks. The biggest one is complexity. Without a proper strategy they can quickly get out of hand and become a maintenance nightmare. Fortunately,  if you follow several good practice and organize the application around features, working with feature flags should be much simpler.

Numerous feature toggles

2. Selecting beans with feature toggle

The most common case for using feature toggles in a Spring Boot application is activating a different implementation of some interface based on a current value of a feature toggle. Let’s examine an example to demonstrate described case.

2.1 Dependency abstraction

Imagine you have a web endpoint which returns a list of products fetched from a database repository. Your goal is to create a feature toggle that allows switching repository implementation to one that uses a web service as a data source.

If the class you want to allow for feature toggling is directly used in other classes, the first thing you need to do is to abstract the dependency using an interface.

The snippet below presents an example product REST endpoint which depends on a ProductRepository interface.

@RestController
@RequestMapping("/products")
class ProductController {

   private final ProductRepository productRepository;

   ProductController(ProductRepository productRepository) {
       this.productRepository = productRepository;
   }

   @GetMapping
   Collection<Product> getAll() {
       return productRepository.findAll();
   }

}

At this moment, we have only one implementation of the interface. Soon we’re going to add another one, which you’ll activate with a feature toggle.

@Repository
class DbProductRepository implements ProductRepository {
    //...
}

2.2 Feature toggle in application.properties

Since the application.properties file is used for configuration of your Spring Boot application, it’s a great place for putting your feature toggle flag.

feature.toggles.productsFromWebService=true

Set the flag to false before committing the code. This way, by default your teammates will have the new feature disabled. If someone wants to activate the feature, their can change the flag value to true on the local development environment.

2.3 Conditional bean creation

Your next step is to create an alternative implementation of the interface that you want to activate with the feature toggle. In order to instantiate the bean based on the value of the created property, you can use Spring Boot annotation called @ConditionalOnProperty. Set the name of the toggle property and the value which should activate it. The value should be the same as the one placed in the application.properties file.

@Repository
@ConditionalOnProperty(
       name = "feature.toggles.productsFromWebService",
       havingValue = "true"
)
class WebServiceProductRepository implements ProductRepository {
    //...
}

Before you start your application, you have to disable the database repository, otherwise, you will get an exception about multiple active implementations of the interface. Return to the the first implementation and apply the following changes:

@Repository
@ConditionalOnProperty(
       name = "feature.toggles.productsFromWebService",
       havingValue = "false",
       matchIfMissing = true
)
class DbProductRepository implements ProductRepository {

We use the same feature toggle name as previously, only its value has changed. Setting the matchIfMissing property is optional. By doing this, if you remove the feature toggle form the application.properties file, this bean will be created even though the value is missing.

3. How to disable controller with feature toggle

You can apply the same strategy to conditionally activate a whole Spring web controller. You don’t need to create an additional interface because there is only one implementation that you want to control with the feature toggle.

@RestController
@RequestMapping("/coupons")
@ConditionalOnProperty(name = "feature.toggles.coupons", havingValue = "true")
class CouponController {
  //...
}

The application.properties should contain the following line.

feature.toggles.coupons=true

When you don’t set the value to true, the controller won’t be instantiated by Spring. The client will simply receive the 404 HTTP status code.

Unfortunately, the @ConditionalOnProperty annotation can’t be used on a single @RequestMapping method. As a workaround, you can move the desired mapping to a separate controller bean. Alternatively, it’s possible to simply inject the value of the feature toggle and create an if statement in the body of the mapping method. However, you should use this solution with caution. If you’re interested why you’ll find the answer in the next paragraph.

private final boolean couponsToggled;

CouponController(@Value("${feature.toggles.coupons}") boolean couponsToggled) {
   this.couponsToggled = couponsToggled;
}

@GetMapping
List<String> listCouponNames() {
   if (!couponsToggled) {
       throw new NotSupportedException();
   }
   //...
}

4. Multiple feature toggle management

As you can read about feature toggles on Martin Fowler’s bliki, feature flags have a tendency to spread across the codebase and can quickly get unmanageable. Even if you have just a few feature toggles in your application, it’s better to abstract the storage of your flags from decision points in which they are used.

4.1 Avoiding feature flag coupling

The last code example from the previous paragraph uses the flag value injected directly from the application.properties file, therefore it doesn’t abstract the storage. If you want to use the same flag in a different part of your application, you’ll have to duplicate the injection.

What you can do instead is to put all feature toggle values inside a single class, which will act as a single source of truth. Using a separate class gives you much more flexibility. For instance, you could replace the storage of flags with a database or implement a mechanism which allows switching flags at runtime.

4.2 Extracting feature toggle decisions in Spring Boot

Once you have a separate bean for your feature toggles, you can easily inject all flags from the application.properties file using the @ConfigurationProperties annotation. Here you can see a sample implementation:

@Component
@Component
@ConfigurationProperties("feature")
public class FeatureDecisions {

   private Map<String, Boolean> toggles = new HashMap<>();

   public Map<String, Boolean> getToggles() {
       return toggles;
   }

   public boolean couponEnabled() {
       return toggles.getOrDefault("coupons", false);
   }

}

The class above will take all properties which start with feature.toggles and put them in the toggles map. As you can see, there’s a method called couponEnabled() which you can use to abstract a feature decision point from the logic behind that decision.

In addition, you’ll also need an extra dependency to enable processing for @ConfigurationProperties.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

5. Actuator endpoint for feature toggles

Since you already have all feature toggles in a single place, all you have to do now is to expose the list using a custom Actuator endpoint. The following example will show you how to do it.

@Component
@Endpoint(id = "feature-toggles")
class FeatureToggleInfoEndpoint {

   private final FeatureDecisions featureDecisions;

   FeatureToggleInfoEndpoint(FeatureDecisions featureDecisions) {
       this.featureDecisions = featureDecisions;
   }

   @ReadOperation
   public Map<String, Boolean> featureToggles() {
       return featureDecisions.getToggles();
   }

}

If you work with the default Spring Boot 2 Actuator setup, the endpoint won’t be exposed via HTTP. In order to test it in your browser, you have to enable the Actuator endpoint by adding its identifier to the web include filter in your application.properties file.

management.endpoints.web.exposure.include=health,info,feature-toggles

Once you run your application, go to http://localhost:8080/actuator/feature-toggles to see the results returned by the endpoint:

Feature toggle in the custom Actuator endpoint

Depending on your needs, you could also implement the possibility to switch feature toggles at runtime using @WriteOperation on the created endpoint. This example covers only the output part.

Conclusion

In this article, you could learn about practical examples of feature toggles in a Spring Boot application. We started with a very basic sample in which the framework covers all the needs. After that, we write some custom code to complete more custom feature toggle requirements. We finished with the helpful Actuator endpoint for displaying the status of all feature flags in the application.

You can find the working sample application in the Github repository. If you like the post and find it useful, please share it with your followers. I’m also looking forward to your questions and comments below the article.

Facebooktwittergoogle_plusredditlinkedinmail

Articles you may like

Advertisement