August 1, 2023

Implementation of Feature Flags Using Strategy Pattern in ASP.NET Core

IT Tips & Insights: Learn how to modify system behavior without changing the source code with feature flags.

By Michal Kubis, Software Engineer 

Feature flags (feature toggles) are becoming a more prevalent standard for modifying system behavior without changing code. In this blog entry, we’re going to look at how they can be coded without losing code testability, readability, breaching single purpose principle, and without adding significant complexity. 

Let’s assume we’re providing an API for weather analyst applications. We deliver detailed atmospheric conditions data from multiple geographical locations. The data we serve comes from our own data gathering points as well as from multiple third-party APIs. Some of them update the results only a couple times a day, so a distributed cache for the responses was introduced. 

Recent usage statistics have shown that the size of responses is becoming an issue as it’s affecting the time to retrieve the data and reduces the number of responses that can be kept in the cache simultaneously. The decision was made to implement cache value compression to kill two birds with one stone. The implementation of the feature needs to be managed by a new feature flag.

Veni

Code snippets below present the codebase before any changes have been introduced. A FeatureFlagService that is responsible for obtaining the current values of feature flags and exposing them to other components.

Feature flag values sources are usually “appsettings” files, database tables or dedicated cloud services. In this sample it’s irrelevant.

A CacheService that wraps the IDistrbutedCache interface to enforce consistent key creation policy across the codebase.

Vidi

After a long benchmarking session, an open-source library k4os was chosen to provide a compression algorithm. To fulfill the requirement of keeping the implementation behind the feature flag, a new dependency was added to CacheService class and a conditional statement to control the flow. Since the feature flag can be toggled independently from cache purging policies, a prefix to the cache key has been added to prevent a situation in which we try to decompress an uncompressed cached value or vice-versa.

These modifications cause some serious consequences: firstly, we need to update the unit tests to cover branching flow. For the same reason, the code just became less readable. We’ve also breached the single purpose principle as our cache service is responsible for the data compression. Let’s refine the solution.

Vici

Strategy pattern seems to be a perfect fit as it’s about selecting an algorithm during runtime. We need an interface and two implementations.

During the pattern implementation it became apparent we can simplify the code further by using a different method for saving data to the cache. A quick glance under the hood of the SetStringAsync from Microsoft.Extensions.Caching.Distributed shows it just converts the string to bytes using UTF8 encoding.

Code below comes from Microsoft.Extensions.Caching.Distributed

One last thing that needs to be taken care of is the selection of correct strategy during DI. As it has scoped lifetime, a strategy will be chosen each time a request is made to the controller that uses the CacheService.

Now the method `ConfigureCacheCompressionStrategy` can be called from Startup.cs in the following way:
services.ConfigureCacheCompressionStrategy();

As with the previous iteration, we still need to update unit tests and write some new ones for the freshly introduced strategy classes. Still, the refined implementation is clearer, more testable, and follows the single-purpose principle. The compression strategies are also loosely coupled with the feature flag and can be freely used by other components of our system. We’ve won on all fronts!

Resources
https://martinfowler.com/articles/feature-toggles.html
https://refactoring.guru/design-patterns/strategy
https://learn.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-6.0
https://github.com/MiloszKrajewski/K4os.Compression.LZ4

About

Hi! I’m Michał Kubiś, a software engineer with 15+ years of experience. My main focus is on Microsoft technologies: .NET, MS SQL Server and recently Azure. I’m a clean code enthusiast and avid board gamer.


Join Our Team

BACK TO MAIN PAGE

Let’s Talk