DEV Community

Cover image for Implementing Feature Flags with Spring: A Step-by-Step Guide for Feature Deployment
bryam vega
bryam vega

Posted on

Implementing Feature Flags with Spring: A Step-by-Step Guide for Feature Deployment

Introduction

Feature flags, also known as feature toggles, are a software development technique that allows teams to enable or disable features dynamically. By decoupling feature deployment from code releases, they provide enhanced control over the application’s behavior and mitigate risks associated with new feature rollouts.

flag

Benefits of Feature Flags:

  • Controlled Rollouts: Gradually introduce new features to a subset of users.
  • A/B Testing: Compare variations of a feature to improve user experience.
  • Quick Rollbacks: Disable malfunctioning features without a full redeployment.
  • Continuous Deployment: Safely deploy code even if certain features are not yet complete.

How to work Feature-flags

Feature flags operate by introducing conditional logic into your application code. Here’s a step-by-step breakdown of how they work:

Step 1: Define a Feature Flag

  • Decide on a meaningful name for your feature flag (e.g., new-feature).
  • Identify the scope of the flag: whether it will be applied globally, for specific users, or for particular environments.

Step 2: Add Conditional Logic

Modify your application code to check the status of the feature flag before executing a feature. This is a simple representation about how could be works a feature flag and could be the most simple way to implement. For example:

if (featureFlagService.isEnabled("new-feature")) {
    // New feature logic
} else {
    // Fallback logic
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Store and Manage the Flag

Use one of the following methods to store feature flags:

  • Configuration Files: Use application properties or YAML files.
  • Remote Service: Utilize a feature flag management tool like Unleash. (For me this is the best way)
  • Database: Store flags in a database for runtime updates.

Step 4: Control Flag States

Dynamically update the feature flag state (enabled or disabled) using the chosen storage method or management tool.

Step 5: Evaluate Flags at Runtime

The application checks the flag’s state dynamically during execution and activates or deactivates features accordingly.

Step 6: Monitor Usage

Use analytics tools or dashboards provided by feature flag services to track the flag’s impact on users and application performance.

How to implement Feature-Flags with Spring Boot and Unleash

This time, we are going to put into practice the use of Feature-Flags using the Spring Boot framework and using the Unleash platform as our flags handler. Quickly, what we are going to do is the following:

architecture

  • Create a service in Spring Boot: This service is going to be a simple API that demonstrates the deployment of feature flags, it is going to have the unleash SDK installed to be able to work with it.
  • We are going to create 2 beans that are going to be our features or different functionalities.
  • Finally we are going to raise an unleash server to configure our flags to enable and disable the functionality to be tested in our Spring service.

Prerequisites

  • Gradle
  • Git
  • Docker
  • IDE of java (IntelliJ, Eclipse …)

Unleash Configuration

In order to configure Unleash, we will download the following repository using the following command:

$ git clone https://github.com/Unleash/unleash.git
Enter fullscreen mode Exit fullscreen mode

After that, we access the folder of the downloaded repository and execute the following command to be able to run our unleash server.

$ cd unleash
$ docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Once up, we access through the address http://localhost:4242 where the credentials are the following:

Username: admin
Password: unleash4all
Enter fullscreen mode Exit fullscreen mode

With this we will be able to access the following platform, and we have the unleash server up and ready for use:

step 1

Create Feature-Flag

To create a flag, we are going to access the default project that is in our unleash server, since we are working with the free version and we cannot create new projects.

step 2

Create new feature flag

Once in the default project, select the new feature flag option. Once this option is selected, we proceed to create our own feature flag. Something interesting as you can see in the image, is that in case we do not want to use the unleash sdk, we can communicate through API requests to the unleash server. In this case our feature flag is going to be called featureFlagExample.

step 3

With our feature flag created we will see the following screen, which contains information about our flag as well as the different strategies we can configure to deploy the features to our end users (that will be another post)

step 4

To activate our feature flag, we will simply activate the development option. These environments will allow us to configure our flags depending on the environment we are in, in this case only development.

Create Project API Key

To be able to communicate with our unleash server and its APIs, it is necessary to create a token that allows us to access its endpoints in order to use our configured flags.

step 5

To do this, we go to Project Settings and in this section we have the option New API token, which will allow us to generate our authentication token to consume our flags.

step 6

We configure it as shown in the image below, if we notice, on the right side of the image, we are presented with how to create an api token through the API unleash, which is also another option.

step 7

This in turn gives us a token created, which we will have to copy anywhere on our computer, since we will only be able to see it that time.

With this, we are now ready to proceed to create our Spring Boot project and configure our feature flag with unleash in our project.

Spring Boot Project (product discount)

A company’s service developed with Spring exposes a REST endpoint that allows consulting the list of available products. The company wants that during discount seasons, the list of products automatically shows the prices with the discount applied, without the end customer having to perform additional calculations. Outside these seasons, the prices should be displayed unchanged.

The project we are going to take as an example is this one: github repo

To download the repository we use the following command:

$ https://github.com/bvegaM/spring-unleash-feature-flag.git
$ cd spring-unleash-feature-flag
Enter fullscreen mode Exit fullscreen mode

We open it in the IDE of preference or the one you prefer, in my case, I will use IntellIJ Idea.

Structure of project

The structure of this project is based on a layered architecture which has the following package tree:

├── HELP.md
├── build.gradle
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── ec
    │   │       └── com
    │   │           └── vega
    │   │               └── spring_unleash
    │   │                   ├── SpringUnleashFeatureFlagApplication.java
    │   │                   ├── config
    │   │                   │   └── SpringUnleashFeatureFlagConfiguration.java
    │   │                   ├── controller
    │   │                   │   └── ProductController.java
    │   │                   ├── domain
    │   │                   │   └── Product.java
    │   │                   ├── repository
    │   │                   │   ├── ProductRepository.java
    │   │                   │   └── impl
    │   │                   │       └── ProductRepositoryImpl.java
    │   │                   ├── service
    │   │                   │   ├── ProductService.java
    │   │                   │   └── impl
    │   │                   │       ├── ProductServiceImpl.java
    │   │                   │       └── ProductServiceWithDiscountImpl.java
    │   │                   └── utils
    │   │                       └── Constant.java
    │   └── resources
    │       ├── application.yaml
    │       ├── static
    │       └── templates
Enter fullscreen mode Exit fullscreen mode

The following project has the following classes and interfaces:

  • SpringUnleashFeatureFlagApplication: Main class of the Spring Boot application. Contains the main method that starts the application. It sets up the Spring context and runs the application.
  • SpringUnleashFeatureFlagConfiguration: Class that contains a bean method to initialize a list of products, which can be injected into the repository as a temporary database.
@Configuration
public class SpringUnleashFeatureFlagConfiguration {

  @Bean
  public List<Product> initProducts(){
    final List<Product> products = new ArrayList<>();
    products.add(new Product("Product 1", "Description 1", 10, new BigDecimal(50)));
    products.add(new Product("Product 2", "Description 2", 30, new BigDecimal(40)));
    products.add(new Product("Product 3", "Description 3", 5, new BigDecimal(30)));
    return products;
  }
}
Enter fullscreen mode Exit fullscreen mode
  • ProductController: Class that exposes REST endpoints for the product service, specifically for retrieving all products.
  • Product: Product class that contains the attributes of the object and also includes a method to calculate the discount for the product.
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Product {

  private String name;
  private String description;
  private Integer quantity;
  private BigDecimal price;

  public Product(Product product) {
    this.name = product.getName();
    this.description = product.getDescription();
    this.quantity = product.getQuantity();
    this.price = product.getPrice();
  }

  public void applyDiscount(BigDecimal discount) {
    BigDecimal discountValue = price.multiply(discount);
    price = price.subtract(discountValue);
  }
}
Enter fullscreen mode Exit fullscreen mode
  • ProductRepository: Repository interface that simulates database transactions and contains a single method.
  • ProductRepositoryImpl: Class that implements the ProductRepository interface and uses the bean created in SpringUnleashFeatureFlagConfiguration to retrieve the list of products.
  • ProductService: Interface that defines the method to retrieve all products. This interface is critical because it sets up the feature flag to determine which implementation to use when the flag is active.
  • ProductServiceImpl: Class that implements the ProductService interface and returns the products.
  • ProductServiceWithDiscountImpl: Class that implements the ProductService interface and returns the products with a discount applied.
  • Constant: Class that contains constant variables.

Install Unleash library

In our downloaded project we go to the build.gradle file and we will notice that in the dependencies section we will have the unleash library installed.

dependencies {
 implementation 'org.springframework.boot:spring-boot-starter-web'
 compileOnly 'org.projectlombok:lombok'
 annotationProcessor 'org.projectlombok:lombok'
 testImplementation 'org.springframework.boot:spring-boot-starter-test'
 testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

 //unleash library
 implementation 'io.getunleash:springboot-unleash-starter:1.1.0'
}
Enter fullscreen mode Exit fullscreen mode

Setting communication service with Unleash server

Once our unleash dependency is installed, we are going to proceed to configure the communication with the Unleash server. To do this we go to the application.yaml and we will see the following configuration.

io:
  getunleash:
    app-name: spring-demo-flag # app name example for unleash (could be any name)
    instance-id: demo-flag-x # instance of service
    environment: development # enviroment Unleash server deploy (in this case is development)
    api-url: http://localhost:4242/api # Unleash server URI
    api-token: <your_token> # token or api key you created in the previous section
Enter fullscreen mode Exit fullscreen mode

Interface to getProducts

This ProductService interface has a method called getProducts() which is decorated with a Toggle (a mechanism for handling feature flags). Below is the explanation of the code:

public interface ProductService {

  @Toggle(name = "featureFlagExample", alterBean = "productServiceWithDiscountImpl")
  List<Product> getProducts();

}
Enter fullscreen mode Exit fullscreen mode

The method getProducts() is a method that returns a list of products (List<Product>). It is a critical method in the product service because it allows retrieving all the products. However, the implementation of how the products are retrieved is linked to the state of the feature flag controlled by the Toggle.

The @Toggle annotation is used to control the behavior of the method based on the activation or deactivation of a feature flag. The feature flag allows changing the functionality of the system without needing to change the code.

  • **name = "featureFlagExample"**: This is the name of the feature flag being used. The value featureFlagExample refers to a flag that is configured in Unleash. If this flag is active, the getProducts() method will execute different logic, in this case, the implementation of ProductServiceWithDiscountImpl.
  • **alterBean = "productServiceWithDiscountImpl"**: Here, it specifies that when the feature flag (featureFlagExample) is active, the implementation to be used for the product service is ProductServiceWithDiscountImpl. This means that, instead of using the default implementation (ProductServiceImpl), the system will use a version that likely includes logic for applying discounts to the products.

Implement differents solutions

We implement the ProductService interface in two classes, these are: ProductServiceImpl and ProductServiceWithDiscountImpl.

// ProductServiceImpl.java
@Service("productServiceImpl")
@RequiredArgsConstructor
public class ProductServiceImpl implements ProductService {

  private final ProductRepository productRepository;

  @Override
  public List<Product> getProducts() {
    return productRepository.getProducts();
  }
}


// ProductServiceWithDiscountImpl.java
@Service("productServiceWithDiscountImpl")
@RequiredArgsConstructor
public class ProductServiceWithDiscountImpl implements ProductService {

  private final ProductRepository productRepository;

  @Override
  public List<Product> getProducts() {
    List<Product> productsWithDiscount = productRepository.getProducts().stream().map(Product::new)
        .toList();
    productsWithDiscount.forEach(product -> product.applyDiscount(Constant.DISCOUNT));
    return productsWithDiscount;
  }

}
Enter fullscreen mode Exit fullscreen mode
  • ProductServiceImpl: In this class, the only thing we do is return the products as they are stored.
  • ProductServiceWithDiscountImpl: In this class, the products are retrieved, and after that, the discount for each product is calculated and added to the new price.

Controller to get Products

Finally, let’s check the ProductController controller that has the endpoint to get all the products. The code is as follows:

@RestController
@RequestMapping("/products")
public class ProductController {
  private final ProductService productService;

  public ProductController(@Qualifier("productServiceImpl") ProductService productService) {
    this.productService = productService;
  }

  @GetMapping()
  public ResponseEntity<List<Product>> getProducts(){
    return ResponseEntity.ok().body(productService.getProducts());
  }
}
Enter fullscreen mode Exit fullscreen mode

As we can see, the @Qualifier annotation is present in the constructor of our ProductController.
The @Qualifier annotation is used to specify which bean implementation should be injected into the ProductController constructor when there are multiple possible implementations of the ProductService interface. In this case, we have two implementations, so we want to use ProductServiceImpl by default.

With everything reviewed so far, we just need to run our code, and we should be able to execute it via http://localhost:8080.

Testing

With this, we are going to perform the tests to validate that our service is working with our Flag feature implementation. The products that are tested with are those created in the class bean **SpringUnleashFeatureFlagConfiguration**.

Testing with flag enabled

In this case we go to our unleash server and activate our flag (by default it is already activated). To activate our feature flag, we only activate the development option which is the environment where the flag must be activated.

test 1

Once our feature-flag has been activated, we proceed to run a test in postman to validate the operation:

test 2

As we can see, the price parameter is shown with the discount already applied, making the first test case correct. Now, let’s test with the inactive flag

Testing with flag disabled

To deactivate the flag, as we did in the previous test, we just deactivate the development option of our flag in our unleash server.

test 3

Once our feature-flag has been deactivate, we proceed to run a test in postman to validate the operation:

test 4

As we can see, the prices in the price parameter are the original prices without applying the discount.

With that, we can say that our feature-flag implementation is working correctly!!! 🚀

celebrate

Conclusion

Feature flags are an invaluable tool for controlling the deployment of new features in a flexible and dynamic manner. They allow teams to enable or disable specific functionality without the need for code redeployments, offering improved control over the application’s behavior.

In this case, we demonstrated how to implement feature flags with Spring Boot and Unleash. By integrating Unleash to manage feature flags, we can conditionally enable or disable certain features like applying discounts to products based on the status of a feature flag. This provides the ability to control the deployment of features progressively, perform A/B testing, and quickly rollback faulty features without affecting the entire application.

In the end, the successful implementation of the feature flag system ensures that we can safely deploy new features, test them with specific user groups, and roll back if needed, all while minimizing risk and disruption.

Happy coding!! ❤️

Top comments (0)