Feature flags aren’t a free lunch. Feature flagging logic can add clutter to your codebase – ugly if/else statements, which can make the path of execution harder to follow, and debugging runtime flow tricker.
Despite these drawbacks, teams still use feature flags because they deliver a ton of value. Engineers can integrate their changes more frequently, avoiding long branches. Feature flags also unlock capabilities like canary releasing, which allows teams to launch features with safety.
So, how do we minimize the cost of feature flagging while still reaping these benefits? One thing we can do is reduce the decision points within our codebase for any given feature flag. Fewer checks mean less feature flagging clutter in our code, without losing any of the benefits of having that feature behind a flag.
The keystone flag is a pattern that helps to reduce the number of flag decision points for a feature by moving those decisions to the boundaries of our system.
The keystone flag idea builds upon a pattern called Keystone Interface.
The idea with keystone interface is that you avoid long-lived feature branches by implementing a feature in a back-to-front fashion, only exposing it in the user interface as the final step. This allows engineers to continuously merge their work into a shared integration branch without having that half-finished work exposed to any users (since there’s no user interface for it). Once the feature is fully implemented, the engineer adds the interface – the keystone – which reveals the feature to users.
We’ll look at how a keystone interface would work for a hypothetical example – adding discount codes to an e-commerce site. While a discount code might seem a simple concept to a consumer, providing this capability is a lot of work behind the scenes. We need to create internal user interfaces for creating and configuring discount codes, teach our payment systems how to apply discounts, display discounts in orders, and so on. However, all of that underlying work won’t get exercised until we give the user a place to enter a discount code. That “Add a discount code” UI is our keystone interface. We can take advantage of that and avoid creating any long-lived feature branches. Engineers can land changes continuously on our main integration branch, and those changes can even be deployed all the way through to production. Until we add the ability to enter discount codes, we don’t have to worry about this half-finished work being exposed.
Testing can present a bit of a challenge with this approach. We’d like to validate all of that under-the-surface functionality, but until the final user interface is in place, we don’t have any way to get at it – that’s the whole point of the keystone interface!
Feature flagging can help here. Rather than leaving the keystone interface unimplemented, we can instead implement it but hide it behind a feature flag, with the flag off by default while the feature is still under development. This is a Keystone Flag – a feature flag protecting a keystone interface.
Keystone flags ensure that the half-finished feature isn’t exposed to our users, but internal users can access it for validation purposes – testing, showcasing, etc. We can even do this testing in production if we want!
A keystone flag unlocks other opportunities beyond testing. Keystone flags also make releasing a feature safer in general. For example, imagine a scenario where our discount code functionality is implemented and tested, so we’ve launched it by turning on the feature for all users, but then we discover a bug. It turns out that we do nothing to prevent the same discount code from being reused multiple times! One of our customers discovers this, and their tweet containing a universal 50% discount code has gone viral! Luckily, the UI to enter that discount code is still protected by a feature flag. We can very quickly disable the UI, preventing a catastrophic loss in revenue.
If we hadn’t used a flag, we’d have to perform an emergency rollback – with all the risks that entails – or even shut down our site entirely while we made an emergency patch. Not good. But thanks to our feature flag, we have a much faster and safer solution. With the discount code UI safely disabled, we can start working on a fix in an orderly fashion.
With keystone flags, we are placing our flagging decision point at the edge of our system – in the user interface itself. We don’t have to limit this approach to user interfaces though; we can apply the same pattern with APIs.
When we place feature flagging decisions at the boundaries of our system, we achieve a major additional win – we minimize the number of flagging decision points in our code. Any discount code functionality further within the system doesn’t need to be directly managed by a flag – we can have it sit latently in production, confident that it won’t be exercised until we’re ready to expose it via the keystone interface.
This is what makes keystone flagging such a valuable practice – it delivers all the safety benefits of feature flagging while minimizing the cruft that those same feature flags can add to your code.
There’s lots more to discuss around how feature flags enable continuous delivery, as well as how to avoid the traps of a messy feature flag implementation. For starters:
- Why Would You Decouple Deployment from Release?
- 5 Best Practices for Testing in Production with Feature Flags
- Managing Feature Flag Retirement and Technical Debt
- Continuous Delivery in the Wild: 4 Successful Practices
- Ultimate Guide to Feature Flags