Feature flags are a software development technique that allows us to enable or disable features in an application without deploying new code. This technique provides greater control over the feature release process, allowing for more flexible and safer updates.
This technique is so widely used in various forms that it has many other names such as feature toggles, feature flippers, and release toggles.
Feature flag use cases
Feature flags have several key use cases that make them invaluable in modern software development.
Canary releases and canary testing
Canary releases and canary testing involve rolling out a new feature to a small subset of users before making it available to the entire user base. This approach helps identify potential issues early on, reducing the impact on the overall system. By using feature flags, we can quickly enable or disable features for specific user groups.
Testing in production and dogfooding
Feature flags allow for safe testing of new features directly in the production environment. This approach helps identify issues that may not surface in staging or testing environments due to unnoticed differences between the environments and the data. By toggling features on and off, developers can gather real-time feedback and ensure the feature behaves as expected under real-world conditions. This approach is the prerequisite for dogfooding.
Rollbacks or Killswitches
In situations where a newly released feature causes problems, a feature flag can serve as an instant rollback mechanism, often referred to as a "killswitch." Instead of deploying new code to remove the feature or writing a hotfix, the feature flag can be toggled off, effectively disabling the feature with minimal disruption.
A/B Testing
Feature flags can also be used for A/B testing by enabling different variations of a feature for different user segments. This allows teams to compare the performance and user engagement between versions, helping inform decisions about which version to fully roll out.
Incremental Rollouts
Instead of releasing a feature to all users at once, feature flags may allow for gradual rollouts. This means you can progressively increase the number of users who have access to a feature, which helps in monitoring system performance and user feedback before a full-scale release.
Benefits of feature flags
Feature flags provide several important advantages that enhance the software development process.
First of all, by separating deployment from release, feature flags minimize the risks involved in deploying new features. If issues arise, a rushed hotfix sometimes can introduce new problems, but toggling the feature off requires no code changes.
Feature flags also allow teams to rapidly gather feedback from real users on new features, enabling A/B tests, canary testing, and dogfooding. All the measures aimed at getting an early feedback loop enable more informed decisions and help fine-tune features before a full-scale release.
Feature flags support
First, you need to determine your strategy in advance: are you planning to deploy all features with feature flags or just some?
If you only need to use feature flags rarely and for certain features, it might be easier to wrap the feature code in a simple IF block in the code, checking a configuration file entry or an environment variable. If the config or environment variable indicates the feature is on, the corresponding code is executed.
Though this method is extremely simple, it's also severely limited: it doesn't provide any monitoring, only allows the feature to be turned on or off, and can't support gradual rollout or user segmentation for targeted feature releases.
Supporting all the capabilities of feature flags is more nuanced and requires significant changes to both infrastructure and architecture. You will need a centralized system to track all features launched with feature flags and their corresponding statuses. This system must allow easy access for updating or monitoring feature flag statuses. To avoid negatively impacting product performance, this system needs to be fast and reliable. If you want to support gradual releases under feature flags, the system must be capable of segmenting users or traffic and correctly routing requests and responses.
There are two main approaches to supporting feature flags: building your own system or using a third-party service such as Unleash or FeatureHub. Both approaches can work, and the engineering team should carefully consider which option best fits their needs.
Basics of using feature flags
First, you need to determine the "scope" of the feature flag — whether the corresponding feature will be applied globally, per user, per group, or based on other criteria—and set its default state. Typically, the default state is set to "off" so that the feature can be deployed first and then activated later.
Next, the new feature code should be encapsulated within a conditional block that checks the state of the feature flag. If you are using a third-party feature flag support tool, it will have detailed documentation on how to wrap your code appropriately. Before deployment, ensure the feature is thoroughly tested in both its enabled and disabled states.
Once the feature flag and associated changes are integrated into the system, the feature is ready to be released. After you release the feature to production, verify that nothing is broken and there are no regressions. If everything is stable, you can enable the feature using the feature flag and adjust its scope as needed.
It's also important to decide whether the feature flag will be retained or if the feature will eventually be made "permanent" and the clear criteria for such a decision has to be made in advance.
If, after some time, you decide to make the feature permanent, remove the feature flag support for it from both the feature flag system and the codebase, and retest everything to ensure continued stability.
How we use feature flags at Qase
At Qase, feature flags are used for all features.
We store our feature flags values in Redis. Since Redis is an in-memory data store, it’s extremely fast and our use of feature flags does not slow the overall app down at all.
When determining whether a feature is enabled for a user or workspace, our system checks Redis for the relevant keys associated with that feature. The feature flag system is hierarchical: it can prioritize workspace-specific flags over user-specific ones, this gives us additional flexibility in terms of rolling out the feature to specific groups, users, and workspaces.
We toggle and fetch feature flags states from the frontend, which communicates with our backend through HTTP controllers. These controllers allow us to easily toggle features on or off and check their current status. For example, a feature can be toggled for an entire user workspace, making it available to all members of that workspace simultaneously.
In terms of implementation, feature flags allow us to maintain different versions of UI components within the application. When a component that includes a feature flag is rendered, the system first checks the status of the flag. Depending on whether the feature is enabled or disabled, the appropriate UI component is displayed. This is handled by a wrapper component on the frontend, which ensures that the correct version of the feature is presented to the user.
While our feature flag system is fast and efficient, monitoring specific to feature flags is somewhat minimal. We may check Redis directly or use internal tools to monitor feature states, but this is typically done on an as-needed basis.
As features mature and become permanent, we “expire” their associated feature flags by removing the conditional logic that controls their display. The UI then always shows the feature as enabled, and the feature flag itself is no longer used, though it may still exist in Redis.
Feature flags in Qase provide us with a powerful tool for managing feature rollouts and ensuring that new developments are introduced in a controlled, measured way. By leveraging feature flags, we can deliver a better, more stable product to our users while maintaining the flexibility to adapt and iterate quickly.