Feature Flags in Spring Boot
Tags: java, spring
Published: 2021-08-17 01:00:50

---
title: Feature Flags in Spring Boot
published: true
description: Decide when things are available, two ways, using Spring's Dependency Injection
tags: Java, Spring
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xmf8thtk86ijqh09jh6x.jpg
---

I was facing a situation at work the other day where I wanted to only show certain features in certain environments. We were still testing out a particular changeset and didn't want it visible to our users in production, but we didn't want to have long lived feature branches... what were we to do?

## Introducing Feature Flags

A feature flag is basically a way of telling a chunk of code whether or not it should be enabled. You can turn on certain features when you're ready and quickly turn them off if need be. 

These flags are usually extracted out of the code layer to configuration files or somewhere else that's easier to access/change. Doing this makes the flags able to be altered by your build pipeline.

## Extracting Flags in Spring Boot

My weapon of choice is Java, more often than not I'm working with the Spring framework. It's basically ubiquitous in Java development now, so let's roll up some feature flags using it. 

We'll put together two ways of serving up different features. One will use Spring [Profiles](https://docs.spring.io/spring-boot/docs/1.2.0.M1/reference/html/boot-features-profiles.html) in order to offer different beans based on where the code is being run. It could give you a different bean when run in your "development" instance versus your "production" environment. 

The other way of going about enabling feature flags is using properties extracted to a flat file on the classpath. This would allow you to change the properties file and not have to affect the codebase at all. 

### Profile Dependent Beans

First we'll define an interface for our different environments. This will lay the groundwork for how we allow different implementations based on the specific profile used.

```java
public interface Environment {
    String getName();

    default Boolean safeToTest() {
        return Boolean.FALSE;
    }
    
}
```
Simple, right? Let the environment define its own name. And then keep a flag to let us know if it's safe for us to test there. By default, assume it's not safe to test. 

Then we'll call out a few different environments...

```java
@Profile("development")
@Component
public class DevelopmentEnvironment implements Environment {
    public static final String NAME = "Development Environment";

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public Boolean safeToTest() {
        return Boolean.TRUE;
    }
    
}
```

This bean implements the `Environment` interface and lets us know that it's safe to test in the `Development Environment`. 

```java
@Profile("production")
@Component
public class ProductionEnvironment implements Environment {
    public static final String NAME = "Production Environment";

    @Override
    public String getName() {
        return NAME;
    }

}
```

It is obviously not safe to test in the `Production Environment`, unless your Bill O'Reilly. 

Now, because Spring Boot handles our dependency injection for us, we can ask for an implementation of the `Environment` and we will be provided by the proper implementation, based on which profile has been passed by the build/runner.

```java
@Service
public class DecisionMaker {
    public static final String decisionFormat = "It is %ssafe to test on the %s.";

    @Autowired
    private Environment environment;
   
    public String canWeTest() {
        return format(decisionFormat, safeToTestString(environment.safeToTest()), environment.getName());
    }

    public static String safeToTestString(Boolean safeToTest) {
        return safeToTest ? "" : "not ";
    }
 
}
```

We can test this out using JUnit and specifying which profile to use in each of our test cases.

```java
@SpringBootTest
@ActiveProfiles("development")
public class DevelopmentDecisionMakerTest {
    
	@Autowired
	private DecisionMaker decisionMaker;
    
	@Test
	void shouldUseDevelopmentEnvironment() {
		String decision = decisionMaker.canWeTest();

		assertThat(decision).as("Should describe the Development environment.")
			.isEqualTo(format(DecisionMaker.decisionFormat,
				DecisionMaker.safeToTestString(Boolean.TRUE),
				DevelopmentEnvironment.NAME));
	}
	
}
```
Check out the full test suite available in the [GitHub Repo](https://github.com/skellas/feature-flags/tree/main/src/test/java/com/sethkellas/featureflags/decisions)

### Property Extraction

Another way of managing these feature flags is extracting out a list of enabled and disabled features to a list in your properties file. We can accomplish this via the use of [ConfigurationProperties](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.typesafe-configuration-properties.java-bean-binding) bindings. This allows us to map directly from our properties file on the classpath to a POJO.

In this case, I'm using a [Record](https://blogs.oracle.com/javamagazine/records-come-to-java) because they're fancy and I've not used them before. But, you could do this with a Lombok [@Data](https://projectlombok.org/features/Data) or just a plain old bean.

```java
@ConfigurationProperties("features")
public record FeaturesAvailable( List<String> enabled, List<String> disabled) {
    @ConstructorBinding
    public FeaturesAvailable(List<String> enabled, List<String> disabled) {
        this.enabled = Optional.ofNullable(enabled).orElse(Collections.emptyList());
        this.disabled = Optional.ofNullable(disabled).orElse(Collections.emptyList());
    }
}
```

You'll notice I've done a bit of extra checking there, to allow for developers to forget to add the values to their properties file. I always find it safe to assume that everyone is as forgetful as I am. It's for the best. 

Now that we have a `FeaturesAvailable` configuration available, let's add that to our `DecisionMaker` and see what we can do with it.

```java
@Service
public class DecisionMaker {

    @Autowired
    private FeaturesAvailable features;
    
    public List<String> availableFeatures() {
        return features.enabled();
    }
    
    public List<String> betaFeatures() {
        return features.disabled();
    }
    
}
```

Simple enough, exposing the list of values that are available and those that are disabled. 

Once again, we'll just put together a quick integration test to show that we can pass in the correct properties. We'll rely on our different profiles again in order to provide different sets of data and get a bit of fun out of our tests.

```yaml
---
spring:
  config:
    activate:
      on-profile: test
features:
  enabled:
     - "Feature One"
     - "Feature Two"
  disabled:
     - "Beta Feature"
---
spring:
  config:
    activate:
      on-profile: development
features:
  enabled:
     - "Feature One"
  disabled:
     - "Feature Two"
     - "Beta Feature"
---
spring:
  config:
    activate:
      on-profile: production
features:
  enabled:
  disabled:
     - "Feature One"
     - "Feature Two"
     - "Beta Feature"
```

We've gone through and specified different lists of `enabled/disabled` based on the active profile. This allows us to write the following tests.

```java
@SpringBootTest
@ActiveProfiles("production")
public class ProductionDecisionMakerTest {
    
	@Autowired
	private DecisionMaker decisionMaker;
    
	@Test
	void shouldReturnAvailableFeatures() {
		List<String> availableFeatures = decisionMaker.availableFeatures();

		assertThat(availableFeatures).isEmpty();
	}
	
	@Test
	void shouldReturnADisabledFeatures() {
		List<String> availableFeatures = decisionMaker.betaFeatures();

		assertThat(availableFeatures).containsExactly("Feature One", "Feature Two", "Beta Feature");
	}
}
```
And
```java
@SpringBootTest
public class TestDecisionMakerTest {
    
	@Autowired
	private DecisionMaker decisionMaker;
    
	@Test
	void shouldReturnAvailableFeatures() {
		List<String> availableFeatures = decisionMaker.availableFeatures();

		assertThat(availableFeatures).containsExactly("Feature One", "Feature Two");
	}
	
	@Test
	void shouldReturnADisabledFeatures() {
		List<String> availableFeatures = decisionMaker.betaFeatures();

		assertThat(availableFeatures).containsExactly("Beta Feature");
	}
}
```

We very quickly see that the different profiles have different values exposed via the `FeaturesAvailable` configuration!

## Summary

I've provided two ways to implement feature flags that don't require a dedicated solution. There are more robust methods for doing this, but sometimes you just want/need to go lightweight. 

If you'd like to see the full implementation, please check out the [GitHub Repo](https://github.com/skellas/feature-flags).