null

Feature flags при помощи GitLab и Unleash SDK

Кто догадается, что тут написано - с меня шоколадка :)

22$zT1D2$ 5`C q~C5 4$j% Á

 

Вместо вступления

Feature flags - заманчивая плюшка, которая позволяет разработчикам включать и выключать функциональность приложения при помощи UI какого-нибудь инструмента автоматизации. Например, поддержка FF есть у GitLab-а в бесплатной версии, что как раз мы сегодня и попробуем. В общем и целом, фиче-флаги могут быть полезны для реализации канареечных релизов (постепенных релизов на часть пользователей), так как позволяют раскатывать фичи не на всех пользователей, а лишь на их часть.

 

Пример реализации

В данном примере рассмотрим, как интегрировать GitLab feature flags в Spring Boot приложение. В качестве языка программирования в примере будет Kotlin, но, очевидно, примеры легко портируются на Java.

Шаг 1

Для того чтобы обращаться к GitLab и узнавать информацию о статусе фиче-флагов нужно добавить в зависимости Unleash client Java SDK. Вообще говоря, Unleash - это софтина, реализующая механизм фиче-флагов, который под капотом использует GitLab. Подробнее о проекте можно почитать в их официальном репозитории.

Шаг 2

Пилим свою абстракцию для feature flags!

enum class FeatureFlags(
    val flagId: String,
) {
    TEST_FEATURE("test-feature"),
    ;
}

interface FeatureFlagsManager {
    fun isFeatureEnabled(featureFlag: FeatureFlags): Boolean
    fun ifFeatureEnabled(
        featureFlag: FeatureFlags,
        action: () -> Unit,
    ) {
        if (isFeatureEnabled(featureFlag)) {
            action.invoke()
        }
    }
}

class GitLabFeatureFlagsManager(
    private val environment: Environment,
): FeatureFlagsManager {
    private lateinit var unleash: Unleash

    @PostConstruct
    fun init() {
        unleash = DefaultUnleash(
            UnleashConfig.builder()
                .appName(environment.getRequiredProperty("feature-flags.gitlab.app-name"))
                .instanceId(environment.getRequiredProperty("feature-flags.gitlab.instance-id"))
                .unleashAPI(environment.getRequiredProperty("feature-flags.gitlab.url"))
                .build()
        )
    }

    override fun isFeatureEnabled(featureFlag: FeatureFlags): Boolean =
        try {
            unleash.isEnabled(featureFlag.flagId, false)
        } catch (e: Exception) {
            log.warn("GitLab feature flags API is not available")
            false
        }
}

private const val STUB_FEATURE_FLAGS_PREFIX = "feature-flags.stubs"

class StubFeatureFlagsManager(
    private val environment: Environment,
): FeatureFlagsManager {
    override fun isFeatureEnabled(featureFlag: FeatureFlags): Boolean =
        getPropertyValue(
            name = keyForFeatureFlag(featureFlag)
        )

    private fun keyForFeatureFlag(featureFlag: FeatureFlags): String =
        "$STUB_FEATURE_FLAGS_PREFIX.${featureFlag.flagId}"

    private fun getPropertyValue(name: String): Boolean =
        environment.getProperty(name, Boolean::class.java, false)
}

@Configuration
class FeatureFlagsProvider {
    @Bean
    @Profile("dev")
    fun stubFeatureFlagsManager(
        environment: Environment,
    ): FeatureFlagsManager = StubFeatureFlagsManager(environment)

    @Bean
    @Profile("prod", "stage")
    fun gitlabFeatureFlagsManager(
        environment: Environment,
    ): FeatureFlagsManager = GitLabFeatureFlagsManager(environment)
}

Из интересного - благодаря наличию интерфейса FeatureFlagsManager мы имеем две реализации логики вычисления значения фиче-флага - для локальной разработки на основе application.properties и для продукционного использования на основе GitLab + Unleash. Ну а свой енамчик с перечислением существующих фиче-флагов - это просто приятно и удобно :)

Шаг 3

Проперти для dev профиля и настройка интеграции с GitLab. Данные для интеграции с GitLab-ом (url, instance-id, app-name) нужно взять из инструкции по конфигурации feature flag, выдаваемым самим гитлабом. Для этого переходим в нужный репозиторий и далее Deploy -> Feature Flags. Там можно создать feature flag, обратите внимание, название флага, задаваемое в гитлабе, должно совпадать со значение  поля flagId соответствующей константы FeatureFlags.

feature-flags:
  gitlab:
    app-name: dev
    instance-id: secret
    url: https://<my-gitlab>/api/v4/feature_flags/unleash/1
  stubs:
    test-feature: true

Шаг 4

Пример использования feature flag-а в коде.

@RestController
@RequestMapping("/api/v1/test/feature-flags")
class TestFeatureFlagsController(
    private val featureFlagsManager: FeatureFlagsManager,
) {
    @GetMapping
    fun testFeatureFlags(): String = if (featureFlagsManager.isFeatureEnabled(FeatureFlags.TEST_FEATURE)) {
        "Feature is enabled"
    } else {
        "Feature is disabled"
    }
}

Шаг 5

Запускаем наше приложение, идем в GitLab (Deploy -> Feature Flags) и играемся с тогллом фиче-флага!

 

Подробности можно почитать в официальной доке GitLab: https://docs.gitlab.com/ee/operations/feature_flags.html#integrate-feature-flags-with-your-application

К тому же в этой доке можно почитать про различные стратегии feature flag-а (на всех пользователей, на процент пользователей и т.д.)

 

P.S. Шоколадка только первому угадавшему!