Unleashing the Power of Feature Flags
"Optimizing Deployment Strategies and Enhancing Agile Development"
Introduction
In today's fast-paced software development industry, the ability to adapt and iterate rapidly is critical.
Enter feature flags, a game-changing technology that allows developers to deliver new features quickly and securely. Feature flags, often known as feature toggles, allow teams to enable or disable functionality without publishing new code. This implies that new features may be sent out to a subset of users for testing, or turned off immediately if problems develop, without interrupting the user experience.
However, feature flags are more than simply an on/off switch for features; they are a strategic tool that may improve your development process, team cooperation, and, ultimately, product delivery to your consumers. In this blog article, we'll look at the basics of feature flags, their benefits, best practices for implementation, and how to include them in your development workflow to promote continuous delivery and innovation.
There are lot of platforms, tools, and libraries to help developers implement this feature flag technique such as LaunchDarkly, Unleash, Togglz, Flagsmith, and ConfigCat but in this article, we will be looking at FF4J.
FF4J, standing for Feature Flipping for Java, is a proposition of Feature Toggle written in Java. It provides extensive support for managing feature flags with a focus on simplicity and flexibility.
Pre-requisites
We will be using Spring Boot as a web framework, the choice is up to the individual as it supports any Java framework.
Spring Boot Application (We are bootstrapping one for demo purposes)
Maven (Gradle is fine too)
JDK21+
Bootstrap Spring Boot Application
We will quickly bootstrap the Spring Boot Application via Spring IO Initialzr to demonstrate usage of FF4J
Add FF4J-specific dependencies as below, this will give access to core FF4J APIs, Web Console, and OpenAPI documentation if you want to play around with FF4J Rest-based APIs.
Updated pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>in.virendraoswal</groupId>
<artifactId>spring-boot-ff4j</artifactId>
<version>1.0.0</version>
<name>spring-boot-ff4j</name>
<description>Demo project for Spring Boot with FF4J</description>
<properties>
<java.version>21</java.version>
<ff4j.version>2.1</ff4j.version>
<springdoc-openapi-starter.version>2.5.0</springdoc-openapi-starter.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.ff4j</groupId>
<artifactId>ff4j-spring-boot-starter-webmvc</artifactId>
<version>${ff4j.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc-openapi-starter.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Now that dependencies are added we will add the required properties in Spring Environment(application.properties) so as to activate the Web Console of FF4J and OpenAPI Documentation
ff4j.web-console.enabled=true
ff4j.web-console.context-path=/ff4j-web
ff4j.api.spring-doc.enabled=true
The idea behind FF4J is you have different types of strategies to toggle or flip the feature. So you have a strategy attached to a Feature which is basically a unit of business functionality to toggle.
There are multiple ways to define FF4J config one via Java API and other via XML. We will be using Java API to define our Strategy and attach it to a Feature and FF4J root object.
There are many out-of-the-box strategies FF4J provides, but we will demonstrate a few as part of an article to get you started.
Release Date Flip Strategy
The "Release Date Flip Strategy" is a feature flipping strategy in FF4J (Feature Flipping for Java) that enables or disables features based on a specified release date. This strategy is particularly useful for scenarios where you want to control the rollout of new features based on their scheduled release dates.
We will define the Release Date Flip Strategy below
FF4j ff4j = new FF4j();
FlippingStrategy releaseDateStrategy = new ReleaseDateFlipStrategy("2024-05-28-14:26");
Feature releaseFeature = new Feature(RELEASE_FEATURE);
releaseFeature.setEnable(true);
releaseFeature.setFlippingStrategy(releaseDateStrategy);
ff4j.createFeature(releaseFeature);
return ff4j;
So basically we define a Feature ID named releaseFeature
and attach a Release Date Flip Strategy.
As per our strategy, our new feature will be released on 2024-05-28-14:26
, until then our code will keep on an old feature.
Here is the simulation code for the same, we define Spring Application Runner which keeps every 5-second check to see if a new feature needs to be released or not as below
FF4jService.java
package in.virendraoswal;
import java.util.Date;
import org.ff4j.FF4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
@Service
public class FF4jService {
@Autowired
private FF4j ff4j;
@Bean
public CommandLineRunner commandLineRunner() {
return args -> {
while (true) {
if (ff4j.check(FF4jConfig.RELEASE_FEATURE)) {
System.out.println(new Date() + " New Feature Release");
} else {
System.out.println(new Date() + " Running on old Feature");
}
Thread.sleep(10000);
}
};
}
}
As soon as time reaches as mentioned above, our logs will start printing New Feature Release
containing output as below
Office Hour Strategy
The "Office Hour Strategy" in FF4J (Feature Flipping for Java) is a feature flipping strategy that enables or disables features based on specific office hours. This strategy allows you to control when features are available to users based on regular working hours, typically aligned with business hours or specific time zones.
This can help with controlling the throughput of the system, or greenzone-specific activities, etc. can be done with this strategy.
We will define the Office Hour Strategy below
FF4j ff4j = new FF4j();
Feature scaleFeature = new Feature(SCALE_FEATURE);
FlippingStrategy officeStrategy = new OfficeHourStrategy();
Map<String, String> officeWindow = new HashMap<>();
officeWindow.put("tuesday", "14:37-14:38,14:39-14:40");
officeStrategy.init(SCALE_FEATURE, officeWindow);
scaleFeature.setFlippingStrategy(officeStrategy);
scaleFeature.setEnable(true);
ff4j.createFeature(scaleFeature);
return ff4j;
So basic idea is you define different sets of time window when you want a certain feature to activate.
In our case above for simplicity, we are activating the feature on Tuesday between 14:37-14:38
, and then 14:38-14:39
apart from this time on tuesday
it won't activate the switch/feature.
For logging purposes, I am checking if the feature is activated every 10 seconds, and as you can see as per logs containing Office Hour Window Activated
were printed between the Office Hour Timing we provided, and vice versa when a window was not activated.
Voila! This way we can do time window-based feature activation in the form of different things.
Region Flipping Strategy
The "Region Flip Strategy" in FF4J (Feature Flipping for Java) is a feature-flipping strategy that enables or disables features based on the geographic region of the user. This strategy allows you to control feature availability based on the location of the user, ensuring that features are only enabled for users in specified regions.
We will define the Region Flip Strategy
FF4j ff4j = new FF4j();
RegionFlippingStrategy regionStrategy = new RegionFlippingStrategy();
Map<String, String> regionParams = new HashMap<>();
regionParams.put("environments", "ASPAC,NAM");
regionStrategy.init(REGION_FEATURE, regionParams);
Feature regionFeature = new Feature(REGION_FEATURE);
regionFeature.setEnable(true);
regionFeature.setFlippingStrategy(regionStrategy);
ff4j.createFeature(regionFeature);
Map<String, Object> initParams = new HashMap<>();
initParams.put("region", "EMEA");
FlippingExecutionContext context = new FlippingExecutionContext(initParams);
ThreadLocal<FlippingExecutionContext> regionContext = new ThreadLocal<>();
regionContext.set(context);
ff4j.setFlippingExecutionContext(regionContext);
return ff4j;
In the above we define key environments
which is mapped to org.ff4j.strategy.RegionFlippingStrategy.INIT_PARAMNAME_REGIONS
to define a set of Granted regions against which features can be activated.
org.ff4j.strategy.RegionFlippingStrategy.PARAMNAME_USER_REGION
defines a single region on which features will be activated. If this parameter is only present in the above environments
the feature won't be activated for that specific region which in our case is EMEA
depicting feature should be activated only for EMEA
region.
As per RegionFlippingStrategy, that org.ff4j.strategy.RegionFlippingStrategy.PARAMNAME_USER_REGION
parameter needs to be set in the Flipping Execution Context.
So when we run the above code, it won't activate the feature which is proved by the Console output containing Feature Active for EMEA Region only
Obviously, we can start with our org.ff4j.strategy.RegionFlippingStrategy.INIT_PARAMNAME_REGIONS
to start to include EMEA
as well, but I want to show how we can flip the feature by web console and include EMEA
so our feature/toggle gets ACTIVE.
Go to http://localhost:8080/ff4j-web/features
You will see all your features, select one you want to edit in our case we will be editing regionFeature.
Click on the Edit option and update EMEA
region as below and click Valid this update and activate toggle as EMEA
is included in grantSet of environments.
Now if we console logs, we can see EMEA
region feature activated
Voila! This way we can enable features based on users located in certain regions.
By default all features and associated strategies metadata are loaded in memory, we can however persist the same to the database so that post-JVM reboots, etc. we maintain the state of our feature flags.
FF4J supports over 20+ database technologies, and it supports Distributed Cache too to persist your feature flags state over the JVM lifecycle.
FF4J Web Console
FF4J web console is available at http://localhost:8080/ff4j-web/
This is based on properties you set in application.properties
we defined earlier in the article.
The FF4J web console is a powerful tool for managing features and properties through a graphical user interface. It provides a convenient way to toggle features on and off, configure feature properties, and monitor feature usage.
With FF4j Web Console we can do following
Manage Features
Manage Properties
Monitor Feature Usage
Advanced Configurations such as Flipping strategies, Group Management etc.
Resources
Thank you for reading, If you have reached it so far, please like the article, It will encourage me to write more such articles. Do share your valuable suggestions, I appreciate your honest feedback and suggestions!