Simple Feature Flags Management

Harish Venkataramanan
Checkout.com-techblog
6 min readOct 14, 2022
Photo by Patrick Perkins on Unsplash

Motivation

Feature flags are one of the most commonly used modern software release practice that improves the velocity of development cycles. There are various benefits to following it, such as

  • Centralized management of features and configuration: The configuration required to check if a feature is enabled/disabled is better placed in a shared store such that various applications rely on a single source of truth.
  • Shipping atomic units of a feature iteratively: A feature that can take a long time to be fully ready can be broken down and released in atomic units and disabled until it is fully ready. This helps in minimizing risks, allowing developers to review changes easily i.e., test new features without having to wait for a full release cycle.
  • Deployment of code independent of feature releases: If a feature should be enabled/disabled while the application is running, we should not be restarting the applications to make the change.
  • No dependency on other teams: Sometimes, a feature may be blocked by work required in a downstream service that we don’t control. In this case, it is better to release with the feature turned off.
  • Turn off buggy code path without rollbacks: If there is an issue with a particular feature that is also downgrading other parts of the system, we should be in a position to minimize the loss quickly
  • Experimentation: Depending on how powerful the feature metadata is we can introduce different ways of comparing data based on different code paths.
  • Developer productivity and faster commits.
  • Segmentation on various dimensions: (users, geography, customers, etc.) Flighting a feature for a certain percentage of the volume of requests helps gradually release it.
  • Systematic feature management decreases errors and improves efficiency

In this post, we can see how to manage such a system simply and effectively.

Schema

The most significant piece of a feature management system is its schema. This is the collection of features’ metadata that determine the capabilities of the system. It also lets the consumers of the system know how to define a feature. Each system is composed of a variety of features, and there is no one size fits all approach. Therefore, it is essential to have a schema that is simple and flexible such that it can be extended in the future if necessary. One such schema is proposed in this gist.

Feature Definitions

In the above gist, the schema contains 2 types of definitions.

  • Boolean definition — Simple true or false feature flags (enabled/disabled)
  • EnabledFor definition — Complex feature flags where one/more filters are used to determine if the feature is enabled/disabled.

Feature filters are what make the schema flexible and powerful. A feature filter is a definition of how a feature should be evaluated to check if it is enabled/disabled. An example is the “Time Window” feature filter where Start and End times defined in the JSON are used to evaluate if the feature is enabled/disabled. Another example is “List Filter” where we can check if an item (available during evaluation) exists in a list. Everything put together will look something like the below snippet:

{
// Define feature flags
"FeatureManagement": {
"FeatureA": true,
"FeatureB": false,
"FeatureC": {
"EnabledFor": [
{
"Name": "TimeWindow",
"Start": "Wed, 01 May 2022 00:00:01 GMT",
"End": "Mon, 01 July 2022 00:00:00 GMT"
},
{
"Name": "List",
"Type": "Users"
"Items": ["User1, User2"]
}
]
}
}
}

In the above example, FeatureC is enabled only for users 1 and 2 and only between the 1st of May and the 1st of July.

Validation of the JSON object we store becomes easier as we have a schema to validate it against.

Evaluation is the step where the actual check for a feature being enabled/disabled happens. Depending on the definition of the feature, this might vary from an in-memory lookup to a call to an external system. It is always advisable to use the in-memory model for evaluations to improve performance and minimize I/O errors.

if (featureManager.IsEnabled(nameof(MyFeatureFlags.FeatureC)))
{
// Do something
}

To improve the performance, cache the evaluation result and refresh the cache appropriately. As an example, consider time window filters. We can cache the evaluation result for a particular feature as true, when the time window starts. This will enable us to not check if the current time is between the window’s start and end, until the window finishes.

SDK

All of the above is possible only if the consumption of the JSON from the configuration store can be deserialized into a list of features metadata. An SDK/client library is necessary to abstract the pipeline of converting JSON into features. The library will provide the methods to check if a feature is enabled/disabled. It should hold the status of each feature in-memory.

Configuration

Ideally, the JSON object adhering to the above schema must be in shared storage that can be accessed by all the apps that require the same set of features. This post primarily deals with configuration for backend development. However, the same applies to the frontend as well. The key difference is in how the configuration is shared between the backend and frontend since there will be security implications based on the types of filters.

Feature management in .Net

The schema proposed above is heavily inspired by the FeatureManagement-Dotnet library but with some interesting changes (The schema proposed here includes Static and dynamic filters, event-based triggers for caching the feature flag results). Although the internals of this library are specific to .Net, the concepts and the schema are universal. The main idea behind this library is the usage of IConfiguration as the provider of the features. This is helpful since IConfiguration has layers (the source of the configuration can be overridden) and can refresh the configuration in memory if any of the configuration source changes. This provides easy mechanisms to fall back on when the remote JSON configuration is unavailable.

Simple Workflow

There are some well-known feature management tools like LaunchDarkly, AWS feature flags, flagship, Unleash, Azure App Configuration, OpenFeatures, etc. All of these are excellent options, and I would advise to choose one of these whenever possible. However, in some cases, it will be impossible to use a third-party product for various reasons like data sensitivity, cost, unavailability of public network, flexibility to define custom feature definitions, performance, etc. In such scenarios, the following workflow can be implemented. It combines the power of Github actions workflows (UI for editing features and approvals) and real-time updates using Redis (remote JSON store).

  • Feature flags JSON should be stored in a GitHub repo. A pull request should be created if a change needs to be made to one/more features.
  • Every PR created/edited/reopened should trigger a GitHub action that performs validation of JSON and other domain-specific checks.
  • The PR should then be reviewed and approved.
  • When the PR is merged, another GitHub action should be in place to trigger the actual deployment of the change in JSON to the corresponding JSON store.
  • The change is propagated to the apps that have subscribed for the changes, and the features collection in memory of those apps are updated.
  • Appropriate permissions in Github (only certain contributors can approve) and in Redis (direct changes to the DB should not be possible) should be in place for better security and access control.

This workflow makes sure the changes are applied only after they are thoroughly scrutinized. Since git is involved, we get an audit, diff, and approvals for free.

Conclusion

Feature management is a powerful release methodology that provides many interesting functionalities. It enables the development team to expose and control features to be activated without doing code deployment, which eventually reduces the risk and saves time. There are various ways to practice it, and we saw one such workflow in this post. We also reviewed a JSON schema to manage the list of features. Every team will already be practicing feature management in one form or another, but doing it in a streamlined, error-free, and flexible manner makes it more effective.

--

--