Feature flags (or feature toggles) is a powerful technique used by modern software teams to manage the behavior of their code in production. Gone are the days when production releases happened occasionally and were a big event. These days, agile teams deliver changes to production continuously, sometimes releasing changes several times a day without any fanfare. Feature flags let teams release their changes to production and stay in full control over who gets to see the changes and when. Using feature flags, you can:
- deploy changes to production but keep them hidden from everyone but internal users.
- gradually roll out features to build confidence.
- if things don’t look good (you discover errors), roll back instantly without any code changes to redeploys.
- test your changes into production.
I’ve been using feature flags for the last 5 years and find them very useful in shipping changes quickly and confidently to millions of users, from infrastructure changes like DB upgrades to UI changes.
In this post, we’ll see how to use feature flags in Java using Spring Boot. Let’s get started.
But first, head over to Unlaunch and create a free account. After you have created a new account, come back and resume reading.
Challenge
Suppose you’re a software developer working on a backend service: User Profile. There is an API that returns user information from the database, combining it with information retrieved from some other services and sources. One day, you were reviewing the performance of the API and noticed it is very slow and takes up to a second to return response.
After some investigation and find the root cause. The delay is being introduced due to the way the service calls other services to get user’s status, open orders and pending cancelations: the calls are being made sequentially, that is, one after the other. You think you can improve the performance by parallelizing the calls.
In traditional software development, you’d create a new feature branch e.g. feature/JIRA-101-async-calls-in-user-profile
. You’d make your code changes and keep committing to this branch. When everything looks good, you’d open a pull request to merge into the development branch. You’d do some more QA and performance testing the QA environment. Finally, your code will be merged into the main branch and your feature will go to staging and then to production. If you find any issues in production, you’d have to create a hotfix into main directly to rollback your changes.
Sounds stressful. If you think about the change you’ve identified, i.e. making asynchronous calls, it is not complicated. But the way software releases are done in large enterprises and the risk of breaking something even with a small change and impacting millions of users, there’s a lot of added friction and stress.
Enter feature flags. Let’s see how we’d utilize feature flags to solve performance issues and release with the peace of mind.
- Create a new feature flag. Turn it off for everyone except yourself. You can do by user id, by environment, or any other criteria.
- Create a new method which makes asynchronous calls to other services.
- In the main method, check if the feature flag is on. 3a: If the feature flag is on: call the new method which makes async calls 3b: If the feature flag is off: Call the old method that you’re trying to replace.
- Enable the feature flag in the QA environment. This will allow everyone using the QA environment to see your changes and test them.
- Merge into the main branch if everything looks good. Enable the feature on your staging environment. Enable it for all internal teams in production but keep it hidden from your users.
- Compare the performance (API response time) of your change to the old API. If everything looks great, gradually roll out the feature to real users.
Let’s see how we can do these steps and use feature flags to release our code to production quickly and confidently.
Solution
1. Set up Feature Flag
If you haven’t already done so, head over to https://app.unlaunch.io/signup and create a new account. Unlaunch is a free feature flag service. As part of account creation, you’d be asked to create a new project. A project is a collection of related feature flags. In this example, we’ll create a new project called “User Profile” as part of registration. By default, each project gets two environments: Production and Test. To keep things simple for this example, we’ll do all work in the Production environment. But separate environments allow you to test changes to feature flags independently.
Make sure that the Production environment is selected using the dropdown on the top left corner of the sidebar.
Figure 1 - Unlaunch Console
Next up, create a new feature. Follow the screenshot below. Here, we create a feature flag with two variations: on and off. In our code, we’ll check this flag. If on variation is returned, we’ll show the new feature (i.e. call the new method we just implemented.) Otherwise, we’ll hide the feature (i.e. use the old code.)
Figure 2 - Create a new flag
Next, we’ll enable the feature flag, that is, serve the “on” variation to only a single user for now. For everyone, else, we’ll serve the “off” variation. Please make sure your setup looks similar to the screenshot below. Also, don’t forget to enable the flag by clicking the green “Enable Flag” button.
Figure 3 - Turn the feature flag on for user id [email protected]
only
At this point, the flag setup is complete. Let’s take a look at how we’d set up the SDK to call this flag and show the new feature.
2. Call Feature Flag in Your Application
Now, let’s use this feature flag in our Spring Boot service to determine whether to use new or old algorithm.
Note: If you are looking for a plain Java example, please see to this repo.
First, add the Unlaunch Java SDK dependency in your pom.xml
(or build.gradle
)
<dependency>
<groupId>io.unlaunch.sdk</groupId>
<artifactId>unlaunch-java-sdk</artifactId>
<version>0.0.8</version>
</dependency>
Next, you’ll need to provide the SDK key to connect to your Unlaunch project and environment. You can find the SDK key for your environment by clicking Settings in the sidebar. Choose the Server Key for the Production environment.
You can paste the SDK key in your application.yaml
or make it available as an environment variable export UNLAUNCH_SERVER_KEY=<paste server key here>
.
unlaunch:
server:
key: PASTE_SERVER_KEY_HERE_FROM_SETTINGS
2a. Initialize the Unlaunch Client
Next, we’ll create and initialize the UnlaunchClient as a bean. When you initialize UnlaunchClient, it will download all feature flags in memory. All evaluations will be done against in memory data store (e.g. HashMap) and are really fast.
@org.springframework.context.annotation.Configuration
public class Configuration {
@Value("${unlaunch.server.key}")
private String sdkKey;
private static final Logger logger = LoggerFactory.getLogger(Configuration.class);
@Bean
public UnlaunchClient unlaunchClient() {
UnlaunchClient client = UnlaunchClient.builder().
sdkKey(sdkKey).
pollingInterval(30, TimeUnit.SECONDS).
eventsFlushInterval(30, TimeUnit.SECONDS).
eventsQueueSize(500).
metricsFlushInterval(30, TimeUnit.SECONDS).
metricsQueueSize(100).
build();
try {
client.awaitUntilReady(2, TimeUnit.SECONDS);
logger.info("unlaunch client is ready");
} catch (InterruptedException | TimeoutException e) {
logger.warn("client wasn't ready " + e.getMessage());
}
return client;
}
}
2b. Evaluate the Flag to Choose New or Old Algorithm
Then in the UserService
class, let’s decide whether to use the new algorithm or not.
@Service
public class UserService {
@Autowired
private UnlaunchClient unlaunchClient;
public User getUserById(String id) {
// Evaluate the feature flag
String variation= unlaunchClient.getVariation("implement-async-calls", id);
if (variation.equals("on")) {
// Call the New algorithm if the flag returns: on
return newAlgorithm(id);
} else {
// Call the Old algorithm if flag returns: off
return oldAlgorithm(id);
}
}
}
One note: The getVariation(...)
method will never throw an exception or return null
. If there is an error such as if the flag is not found or the initial sync fails, it will return a special String value: control. So you don’t have to worry about adding any additional error or checks around this method and can safely use it everywhere.
3. Try it out
You can find the complete source code for this project on GitHub. You can clone and run it locally using mvn spring-boot:run
or (gradlew bootRun
).
To test the feature flag, call the API with any user id e.g. [email protected]
. Because we disabled the flag for all users except [email protected]
, the old algorithm will return. http://localhost:8085/api/v1/users/[email protected]
Now let’s call the same API with [email protected]
which should return the on variation and hence the new algorithm. (See Figure 3.) http://localhost:8085/api/v1/users/[email protected]
That’s all. If you enjoyed this post, please share it.