Skip to main content

Feature Flags

Feature flags are one of many tools that make up a robust release management strategy and are critical to moving towards a true Continuous Delivery pipeline. They are particularly important to mobile app development, and usage in a mobile app environment comes with it's own additional set of caveats.

All work on a new feature should be done behind a feature flag to ensure that it can be 'safely' delivered to end users.

Creating a feature flag

Config Files Info

Feature flags are currently created by setting a value in the frontend project's config files. There's a few important things to note when considering these files:

  • Different files will be used for different environments. This is relatively intuitive; for example, 01-ios will be used on iOS and not Android, and configs ending with -uat will only be used in the UAT environment.
  • They are applied and overridden in ascending order. This means that a file prefixed with 01 is applied first, and a file prefixed with 05 is applied after. This allows us to set our feature flags to different values in different scenarios.

Example

With the above in mind, creating a new feature flag is as simple as adding an entry in the [dynamic.domain.features] block of a config file. A new feature flag might look like:

# 00-global.toml - disabled globally, much like a default value
[dynamic.domain.features]
some-new-feature = false

# 02-uat.toml - enabled in UAT for testing
[dynamic.domain.features]
some-new-feature = true

# 02-local.toml - enabled locally for dev
[dynamic.domain.features]
some-new-feature = true

For more info on how Config Files and feature flags in those files are handled, have a look at the frontend project's notes on the topic.

Semver Flags

When rolling out a feature developed behind a feature flag, it's encouraged to only release it to users with a specific version (or later) of the app. By specifying the version of the app our feature was completed, we ensure that users w/ a previous version of the app don't accidentally receive an incomplete or erroneous version of the feature. This can be done by setting config values to the desired version:

# this would release our feature to users running >= 8.22.0
[dynamic.domain.features]
some-new-feature = "8.22.0"

Deploying a feature flag change

When first adding or updating feature flags in the frontend repo a separate process can be followed to speed up deployment.

Config changes only are not required to go through the full frontend process and can be deployed independently as follows:

  1. Setup a branch prefixed by config/ (eg. config/XXX-322)
  2. Commit the relevant flag changes (eg. config(yourfeaturename): Enable Production for Ladbrokes)
  3. Submit an MR and gather the required approvals
  4. When the MR is ready to merge, ask your TL to coordinate an ad-hoc release, this involves:
  • Merging the MR
  • Triggering the pipeline to Publish UAT.
  • Checking in UAT if required
  • Posting an update to the #deployments channel regarding the UAT deployment
  • Triggering the pipeline to Publish production if required.
  • Posting an update to the #deployments channel regarding the Production deployment

Using a feature flag

Local Development Caveat

If you wish to develop with a feature flag not yet added to the configuration files, you'll need to provide it via your project's config-overrides.json file. Example:

{
"domain": {
"features": {
"some-new-feature": true
}
}
}

React Hook

There's a useFeatureFlag hook available from @app/hooks/useFeatureFlag that can be used to conditionally run code or execute logic. Example:

const isEnabled = useFeatureFlag("some-new-feature");

return isEnabled ? <SomeNewFeature /> : null;

Non-React Utilities

If you need to access a feature flag outside of React, there's a feature utility available from @app/utils. Example:

const isEnabled = feature("some-new-feature");

if (isEnabled) {
items.push({
/* some data for our new feature */
});
}