question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

A better way to add feature flags

See original GitHub issue

The Problem

Currently, we declare and inject a configuration class via the dependency injection (DI) layer whenever we need to define a feature flag.

data class PhoneNumberMaskerConfig(
    val proxyPhoneNumber: String,
    val phoneMaskingFeatureEnabled: Boolean
) {

  companion object {

    fun read(reader: ConfigReader): PhoneNumberMaskerConfig {
      return PhoneNumberMaskerConfig(
          proxyPhoneNumber = reader.string("phonenumbermasker_proxy_phone_number", default = ""),
          phoneMaskingFeatureEnabled = reader.boolean("phonenumbermasker_masking_enabled", default = false)
      )
    }
  }
}

This is problematic for a few reasons:

  • It is a lot of overhead to create a new config class if one does not already exist for a feature.
  • The configuration objects are scoped to a particular package, but sometimes a feature flag cuts across multiple packages. This leads to leakage of information across boundaries.
  • It is very cumbersome to inject an entire class just to get access to a feature flag.
  • There is no way for us to find all the feature flags being used across the codebase easily.

We need a better solution, one that can solve these issues while retaining the benefits of the existing solution:

  • Be able to configure these flags in tests
  • Be able to switch between local and remote flags
  • Override these flags for local development

Proposal

We define features using enums, and provide a default value.

enum class Feature(
    val enabled: Boolean,
    val remoteConfigKey: String = ""
) {
  SecureCall(false),
  Telemedicine(true, "telemedicine_enabled")
}

Once this is done, we can define a singleton which can be used throughout the app to get access to the feature flag values:

object Flags {

  // Set this during app/test initialization
  lateinit var remoteConfig: ConfigReader

  fun isEnabled(feature: Feature): Boolean {
    return when {
      feature.remoteConfigKey.isBlank() -> feature.enabled
      else -> remoteConfig.boolean(feature.remoteConfigKey, feature.enabled)
    }
  }
}

With this, we can avoid all the boilerplate around creating a class and injecting it and simply call Flags.isEnabled(SecureCallEnabled) to check whether a feature is enabled or not. All our feature flags will also be defined in a single place.

The only thing missing from the list of desired benefits is a way to override the values in tests, which can be solved by maintaining a hash map inside the Flags object.

var overrides = mutableMapOf<Feature, Boolean>()

fun isEnabled(feature: Feature): Boolean {
  return when {
    feature in overrides -> overrides.getValue(feature)
    feature.remoteConfigKey.isBlank() -> feature.enabled
    else -> remoteConfig.boolean(feature.remoteConfigKey, feature.enabled)
  }
}

Overriding this value in tests is very simple.

@Test
fun `the feature is overriden in this test`() {
  Flags.overrides[Feature.SecureCall] = true
}

What does everyone else think? Is this something that appeals to us as a team? Any other solutions people can think of?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:3
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
vinaysshenoycommented, Jun 8, 2020

Just to confirm, this change is only affecting the way we create feature flags, We still need to use separate classes when reading certain remote config flags that are not feature flags right?

Yes, we might still use the same remote configuration service to manage the flags, but the way we access feature flags versus remote config values will be different. The way to access remote config values will remain unchanged for now.

1reaction
vinaysshenoycommented, Jun 1, 2020

This is so much easier 🙌 and we will have a single source for all feature flags :shipit:

Could you also please think of possible problems we might have with this approach?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Coding with Feature Flags: How-to Guide and Best Practices
It is a good practice to keep feature flags are short-lived as possible. Keeping their number low is equally important. A feature flag...
Read more >
Feature Toggles (aka Feature Flags) - Martin Fowler
Feature Toggles (often also refered to as Feature Flags) are a powerful technique, allowing teams to modify system behavior without changing code. They...
Read more >
Feature Flags | Atlassian
Feature flags is a software technique that enables teams to make changes without additional code. Read about benefits, use cases and more.
Read more >
Feature Flags: what they are and how to use them - Flagship.io
Feature flags help you decouple code deployment from releases to speed up feature releases while mitigating risks.
Read more >
Essential Guide to Feature Flags - Split Software
Feature flags allow you to scientifically validate your product ideas. A good feature flagging system allows a product manager to slice and dice...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found