OpenFeature in .NET
Introduction to OpenFeature in .NET
January 25, 2025
Overview
Feature flagging is a mechanism through which features or changes can be toggled on or off within an application, typically without redeploying the code. This would involve adding a package from a vendor, such as Split.IO, and adopting their provided SDK, or APIs, in your codebase. OpenFeature is a community-driven specification that provides a common, vendor-agnostic, API for feature flagging. This post explores why you would want to feature flag and how you can use OpenFeature in .NET.
Why Feature Flag?
Releasing new features to clients can often be cumbersome. Big bang waterfall-style releases are not ideal for a number of reasons, but for the purposes of this post we will on one: lack of customer engagement or feedback. Software changes are not handed to the customer until the very end development cycle, which means a huge amount of time has been invested before any user can get their hands on the new feature.
Releasing changes, bug fixes, and new features more iteratively (say over sprints) mitigates this by giving opportunities to the project stakeholders to get involved early on. However, shipping changes are potentially still all or nothing. Breaking work down into sprints means the MVP (minimum viable product) gets built more iteratively but it does not necessarily mean the changes are shipped incrementally. Shipping frequently, or achieving continuous delivery, means improved code quality - as issues are identified and rectified early - and reduced risk1.
This is where feature flags come in. The development team can ship changes every day while the feature remains hidden and out of sight. Only once the flag is toggled will the new feature show itself. Feature flags can manifest themselves as a simple configuration that is loaded during a build or deployment - maybe through an environment variable. They can also be sourced remotely, using a vendor like Split.IO or PostHog, to enable a more dynamic approach to feature management.
Sometimes you just want to gather some data on why a button should be blue instead of grey, or that searching over a list of items is easier than paginating through the entire collection. Feature flags allow you to finely tune which groups of users see the control, and which see the variable.
In both uses of Feature Flags, they can provide valuable insights to the Product team. This leads to more data-driven decisions being made and greater confidence in the software that is being shipped. This is good for the development team, as time is not wasted on fruitless projects. It is also good for the project stakeholders, as customers continue to see more value in the software they are using.
Code Set up
The following assumes you have the .NET 9 (or greater) SDK installed and a basic understanding of C#.
To get started you can run the following in a shell to generate a basic webapi.
dotnet new webapi --output openfeaturedemo
We need to also add the OpenFeature .NET SDK to the project with NuGet.
dotnet add package OpenFeature OpenFeature.DependencyInjection OpenFeature.Hosting
This will add the project references to the project file and allow the .NET SDK to resolve references to OpenFeature. Next, we can extend the generated sample and add the necessary bits to make OpenFeature work.
Using your preferred code editor open the Program.cs file. You will find a basic weather forecast Web API that returns 5 randomly generated forecasts. We will modify the generated code and add a more-forecasts feature flag that will enable us to return more weather forecasts.
The code below will set up OpenFeature with dependency injection so that we can resolve the necessary classes and interfaces throughout our program.
|
|
Now that we have initialised OpenFeature with an InMemoryProvider, we can now use OpenFeature in the /weatherforecast API. Add the following using statement to the Program.cs file so that we can leverage the [FromServices] attribute to resolve the injected OpenFeature client.
|
|
With the .NET Minimal API we can inject any necessary services and use them within the delegate. Below we inject the IFeatureClient and try to get the more-forecasts feature. We default to false if the feature is not found, which will effectively leave the API in the same state prior to the toggle being added.
|
|
Now that the necessary OpenFeature changes have been added we can test that our API is still functioning. You can use the dotnet run command to start the process.
dotnet run
You can open a browser to the URL displayed in the shell output and send a request to /weatherforecast. You should see 5 weather forecasts. Why is this? When we set up the InMemoryProvider we set the “on” variant for the more-forecasts feature flag to return false. We can modify this so that the variant returns true instead.
|
|
When we rerun our application we will see 10 forecasts instead of 5. We have a working feature flag!
Toggles like this are really easy to work with. However, instead of arbitrarily turning on and off a feature we can extend this and use another type of Flag. Let’s make the configuration of how many forecasts are shown more dynamic. Below we update the feature flag name to be number-forecasts and instead of using a Flag<bool> we use an integer instead. The default variant here will be “10”, although we could pick from any of the three variants.
|
|
Next we can make a small change to the API to use the new integer-based feature flag.
|
|
Now we have much more flexibility over how many forecasts are shown, and if we want to add new variants we can do so without changing the API at all. If we replaced the InMemoryProvider with a remote provide, like Flagd, we could configure the feature flag without changing the code at all.
Advanced Feature Flags
Let’s say that we want to be able to configure both the number of forecasts and the summary text for each forecast. We could achieve this with two separate feature flags, one as an integer flag and another as a string. Instead, we can leverage the Value and Structure types to build a single flag that can handle both use cases.
First, we need to add a new using statement to ensure we can reference these complex types.
|
|
Next, we can tweak the InMemoryProvider to set up a more advanced forecasts flag.
|
|
Finally, the API needs to be extended to be able to handle this new Object type of feature flag. We can comment out the outer summaries string array as this is no longer needed.
|
|
Next, we can tweak our client method call and replace GetIntegerValueAsync with GetObjectValueAsync. We need to check if the feature IsStructure so we can extract the necessary bits of feature toggle data. In this example I have omitted any null and safety checks, in Production, you should add some defensive code to prevent unexpected errors from occurring.
|
|
Group A in this example gets 3 forecasts with colder summaries, while Group B gets 6 forecasts with warmer summaries. At the moment though, which group gets shown what is kind of irrelevant. We do not provide any context about the request or user. So we cannot easily identify which user belongs to which group. To resolve this we will extend the sample once more. We will take a specified X-Group request header, and use this to build up a context to pass to OpenFeature.
|
|
You may need to use a tool like Postman or Visual Studio built-in support for .http files to be able to send a request to /weatherforecast with the X-Group header. We now have our two groups being shown different feature states, based on context provided by the request. While this example is relatively simple, you could evolve this to include more context (region, data centre, language, etc…). You could build some middleware that creates an EvaluationContext on the fly and passes it along to the request delegate, reducing any boilerplate you may find yourself adding to your API actions or controllers.
-
The DevOps Handbook by Gene Kim, Jez Humble, Patrick Debois & John Willis covers Continuous Delivery and its benefits but within the context of DevOps. ↩︎