If you aren’t using Feature Flags in your software project yet, you should start right away. It’s the key to unlocking Continuous Deployment in production, as well your best friend in managing release risks. I won’t belabor the point here, but checkout the excellent guides from Atlassian and LaunchDarkly. The good news is that no matter what stack you are working in, there’s probably an existing framework for managing flags.
One of my favorite implementations for the JVM is Togglz. Let’s walk through the setup of Togglz in a Sprint Boot + Kotlin project.
I’m using Intellij Idea for access to it’s easy Spring Boot starter. We’ll create a project with Spring Web dependencies.
For this example, we’ll just build a RESTful service, starting with a really simple controller.
package com.example.springkotlintogglz.controllers import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController @RestController class HomeController() { @GetMapping("/") fun index() = "Hello, World!" }
Run your app, and you should see it working, like so:
Great, lets add some feature toggles. Start by adding the following lines to the dependencies section of you build.gradle.kts
implementation("org.togglz:togglz-spring-boot-starter:3.1.2") implementation("org.togglz:togglz-kotlin:2.8.0")
We’ll also need a Kotlin configuration file – TogglzConfiguration.kt
We’ll add a single feature flag, controlled through a code-level enum. I know, the point of feature flags is to allow changes without a code release, we’ll get to that later, for now, we’ll code the flag into the enum to get a feel for the code.
package com.example.springkotlintogglz import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Primary import org.togglz.core.context.FeatureContext import org.togglz.core.context.StaticFeatureManagerProvider import org.togglz.core.manager.FeatureManager import org.togglz.core.manager.FeatureManagerBuilder import org.togglz.core.repository.StateRepository import org.togglz.core.repository.file.FileBasedStateRepository import org.togglz.core.spi.FeatureProvider import org.togglz.core.user.NoOpUserProvider import org.togglz.core.user.UserProvider import org.togglz.kotlin.EnumClassFeatureProvider import java.io.File @Configuration class TogglzConfiguration { @Bean fun featureProvider() = EnumClassFeatureProvider(KotlinTestFeatures::class.java) @Bean fun stateRepository() = FileBasedStateRepository(File("/tmp/togglz-features.properties")) @Bean fun userProvider() = NoOpUserProvider() @Bean @Primary fun myFeatureManager(stateRepository: StateRepository, userProvider: UserProvider, featureProvider: FeatureProvider): FeatureManager { val featureManager = FeatureManagerBuilder() .featureProvider(featureProvider) .stateRepository(stateRepository) .userProvider(userProvider) .build() StaticFeatureManagerProvider.setFeatureManager(featureManager) return featureManager } } enum class KotlinTestFeatures { HELLO_WORLD; fun isActive(): Boolean { return FeatureContext.getFeatureManager().isActive { name } } }
Now that we have the supporting code in place, we can modify our controller to use the flag. Update your HomeController.kt
to alter the behavior based on the flag state.
package com.example.springkotlintogglz.controllers import com.example.springkotlintogglz.KotlinTestFeatures import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RestController @RestController class HomeController() { @GetMapping("/") fun index(): String { if (KotlinTestFeatures.HELLO_WORLD.isActive()) { return "Hello, World!" } else { return "Not Implemented" } } }
Run your application, and you should get a “Not Implemented” output.
To confirm that these can actually “toggle”, lets turn this flag on. Update your TogglzConfiguration.kt
to match the code below. In this simple example we are employing the @EnabledByDefault
annotation.
enum class KotlinTestFeatures { @EnabledByDefault HELLO_WORLD; fun isActive(): Boolean { return FeatureContext.getFeatureManager().isActive { name } } }
There you have it, the simplest setup to show that the Togglz library is working for us. Given that everything is hard coded, the value is limited, but in Part 2, we’ll make this production quality by introducing a data store & an admin interface.