Ep2: Continuous Delivery - Feature flags, another way to make code changes deployable
In the previous episode, we described what deployable means in terms of Continuous Delivery:
However, there is another way to make code changes deployable. Let’s talk about an elegant technique called Feature flags.
Deploy a new code without activation
People typically think that code deployment equals to rolling out a new feature or changing the behavior. But, deployment and enablement don’t have to happen at once. You can deploy the new code without activation by using Feature flags like below:
function main() {
const featureFlags = fetchFeatureFlags();
...
if (featureFlags.enableCoolFeature) {
executeCoolFeature(); // The new code
}
...
}
Feature flag is a small configuration available at runtime of your application. It’s typically injected dynamically like the code above so that you can modify the value of feature flag without code deployment. Since the default value of such boolean flag is normally false
, this code is deployable by nature (!) because the new code won’t be executed until you change the value of the feature flag.
Feature flags isolate deployment and release
Feature flag brings lots of benefits in terms of operations. Since you can control when you activate the new feature without deployment, your deployment itself is regression resilient. Any deployment won’t introduce new behavior so that it’s ideally identical to the code before the deployment.
This makes Continuous Deployment a.k.a. (another) CD easier and rollback is rarely needed. With a large code base application, this means that your code is most likely able to be deployed to production along with other teams’ changes without any issue. Without feature flags, your change could be rolled back by a bug introduced by the other team’ change or vice versa.
Feature flags allow both teams to release their own new features independently. If either team notice some issues with their own new feature, they can easily rollback their own feature exclusively. This isolation is huge win especially for large organizations.
You can even release the new features gradually. For example, you can enable the feature for 1% of the users first and monitor their behavior to verify whether it’s working correctly. Once confirmed, you can dial up the percentage every day up to 100%, for example.
However, feature flags are not silver bullets
Although feature flags look perfect for CD, you can’t deploy every single change under feature flags. For example, updating dependency libraries is not easy to hide behind feature flags.
The other downside of feature flags is that the number of combinations of multiple feature flags can be exploded. Theoretically, every single combination could behave differently at runtime but it’s much harder to validate all the combinations. Therefore, you might see a weird behavior uncovered by tests because of a rare combination of existing feature flags.
So, you need to carefully select which features should be under feature flags and monitor the usage of feature flags regularly. If a feature flag has been enabled for all users for a long time, it’s safe to delete the feature flag as well as the unused old code.
Lastly, even the new feature is under a feature flag, you must run the same validations steps to ensure you don’t have known regressions like integration tests. The application behavior could change by just loading a new library without calling it e.g. it might load a too large object into memory without any function calls.
Summary
Feature flags are powerful tool to make a new code deployable instantaneously. You can deploy the new code without changing the current behavior at all, then enable it by turning the feature flag on later. This can isolate multiple teams’ feature releases each other so that unnecessary rollbacks rarely happen. However, it’s not silver bullet and you need to carefully use it, monitor its usage and keep running the validations steps like integration tests.