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 with05
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:
- Setup a branch prefixed by config/ (eg.
config/XXX-322
) - Commit the relevant flag changes (eg.
config(yourfeaturename): Enable Production for Ladbrokes
) - Submit an MR and gather the required approvals
- 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 */
});
}