The Feature Management System is a comprehensive solution for managing feature flags and experiments in .NET applications. It provides a flexible, extensible framework that combines static configuration-based features with dynamic, customer-targeted experiments, enabling sophisticated feature rollout strategies while maintaining simplicity for basic use cases.
Built on top of Microsoft's Feature Management library, this system extends the base functionality with additional capabilities including experimental features, HTTP context integration, and advanced filtering options.
- Feature Flags: Toggle features on/off without code deployment
- A/B Testing: Run experiments with customer targeting
- Multiple Filter Types: Time window, percentage, market, campaign, and more
- Runtime Overrides: Temporarily enable/disable features via query parameters
- Frontend Support: Automatic handling of frontend-specific features
- Custom Filters: Extensible filter system for custom logic
- Comprehensive Observability: Built-in logging and distributed tracing
dotnet add package Microsoft.Omex.Extensions.FeatureManagementservices.AddOmexFeatureManagement(configuration);It is recommended that you follow this call with calls to register explicit custom implementations of IExperimentManager and IIPRangeProvider to override the default empty implementations. For example:
services.AddSingleton<IExperimentManager, MyExperimentManager>();
services.AddSingleton<IIPRangeProvider, MyIPRangeProvider>();To consume the Feature Management, consume one of the following interface from your codebase:
IFeatureGatesConsolidator(Higher Level)IFeatureGatesService(Lower Level)
Both interfaces provide comprehensive documentation.
It is recommended to use IFeatureGatesConsolidator where possible to simplify usage.
This implementation uses a layered approach to feature resolution, allowing for multiple sources of truth. The order of precedence is as follows:
- Query-String Overrides (Highest Priority)
?features=Feature1;Feature2: Enable features?blockedFeatures=Feature3: Disable features
- Experimental Features
- Customer-targeted experiments
- A/B test allocations
- Static Configuration (Lowest Priority)
- Features defined in
appsettings.json - Default values
- Features defined in
If you have an experimentation service available, you can make runtime configuration changes to override the static configuration. You will need to inherit from IExperimentManager and pass the class when calling ConfigureFeatureManagement(). By default, no experimentation service will be used.
FeatureOverrideSettings.Disabled and FeatureOverrideSettings.Enabled turn a feature on or off regardless of the filters. If a feature is added to both settings, FeatureOverrideSettings.Disabled takes precedence.
FeatureOverrideSettings.Toggled allows the enablement of a feature committed as disabled but with the Toggle filter present. On activation, this will evaluate the applied filters.
Static configuration is defined within appsettings.json using the following format:
"FeatureManagement": {
"FeatureA": true,
"FeatureB": false,
"FeatureC": {
"EnabledFor": [
{
"Name": "Campaign",
"Parameters": {
"Enabled": [
"Campaign1"
]
}
}
]
},
}FeatureAlwaysOnA and FeatureAlwaysOnB are always-on features, where no filters will be applied:
"FeatureAlwaysOnA": {
"EnabledFor": [
{
"Name": "AlwaysOn"
}
]
},
"FeatureAlwaysOnB": trueFeatureAlwaysOffA and FeatureAlwaysOffB are always-off features, where no filters will be applied:
"FeatureAlwaysOffA": {
"EnabledFor": []
},
"FeatureAlwaysOffB": falseThe campaign filter considers the campaign query-string parameter. Disabled takes precedence over Enabled, so Campaign2 is disabled in the example below.
"FeatureCampaignA": {
"EnabledFor": [
{
"Name": "Campaign",
"Parameters": {
"Disabled": [
"Campaign2"
],
"Enabled": [
"Campaign1",
"Campaign2"
]
}
}
]
}The IP address filter checks the caller's IP address against a predefined range.
"FeatureIPAddressA": {
"EnabledFor": [
{
"Name": "IPAddress",
"Parameters": {
"AllowedRange": "MYRANGE"
}
}
]
}No ranges are defined by default. To define a range, inherit from IIPRangeProvider and pass the class when calling ConfigureFeatureManagement().
The market filter considers the market query-string parameter. Disabled takes precedence over Enabled, so US is disabled in the example below.
"FeatureMarketA": {
"EnabledFor": [
{
"Name": "Market",
"Parameters": {
"Disabled": [
"US"
],
"Enabled": [
"US",
"IE"
]
}
}
]
}The percentage filter is provided by the library.
"FeaturePercentageA": {
"EnabledFor": [
{
"Name": "Percentage",
"Parameters": {
"Value": 50
}
}
]
}The time window filter is provided by the library.
"FeatureTimeWindowA": {
"EnabledFor": [
{
"Name": "TimeWindow",
"Parameters": {
"Start": "2020-01-01T00:00:00Z",
"End": "2021-01-01T00:00:00Z"
}
}
]
}Additional settings are available, which can be examined within the library.
The codebase provides two filters that facilitate the combination of multiple filters: ParentFilter and ToggleFilter.
The parent filter allows a feature to inherit the enablement status of another feature. This is useful for creating hierarchical feature structures.
An example of the filter is shown below, although this example does not make sense in production as it creates FeatureParentA that is just identical to ParentFeature.
"FeatureParentA": {
"EnabledFor": [
{
"Name": "Parent",
"Parameters": {
"Feature": "ParentFeature"
}
}
]
}The toggle filter enables you to pre-define complex feature logic in appsettings.json while keeping the feature disabled by default. This pattern is particularly useful when you want to prepare sophisticated feature configurations (with multiple filters, conditions, and rules) ahead of time, then activate them on-demand without modifying the configuration.
How it works:
- Define your complete feature logic in
appsettings.jsonwith all necessary filters (time windows, percentages, markets, etc). - Add the
Togglefilter to keep the feature disabled by default. - Activate the feature at runtime through:
- The
toggledFeaturesquery-string parameter (semicolon-separated list). - Or your experimentation service (see Experimental Features).
- The
This approach allows you to specify and deploy complex feature logic that could not be defined through query-string parameters alone. When activated, the feature evaluates all its configured filters normally – the toggle simply acts as a main switch.
"FeatureToggleA": {
"EnabledFor": [
{
"Name": "Toggle"
}
]
}The filters can be combined using a RequirementType property, which can be set to either All or Any. The default, if none is specified, is Any.
FeatureCombiningA is enabled if both ParentFeature is enabled and the market is US.
"FeatureCombiningA": {
"RequirementType": "All",
"EnabledFor": [
{
"Name": "Parent",
"Parameters": {
"Feature": "ParentFeature"
}
},
{
"Name": "Market",
"Parameters": {
"Enabled": [
"US"
]
}
}
]
}FeatureCombiningB is enabled if ParentFeature is disabled.
"FeatureCombiningB": {
"RequirementType": "All",
"EnabledFor": [
{
"Name": "Parent",
"Parameters": {
"Feature": "ParentFeature"
}
},
{
"Name": "Toggle"
}
]
}FeatureCombiningC is enabled if either the campaign is Campaign1 or the market is US.
"FeatureCombiningC": {
"RequirementType": "Any",
"EnabledFor": [
{
"Name": "Campaign",
"Parameters": {
"Enabled": [
"Campaign1"
]
}
},
{
"Name": "Market",
"Parameters": {
"Enabled": [
"US"
]
}
}
]
}The framework's extensibility allows for the creation of custom filters that can be applied to features.
Custom filters implement the IFeatureFilter interface. FeatureFilterEvaluationContext is passed to the filter by the framework, which is based on the IConfiguration system and allows mapping the filter config to the filter's settings type.
MarketFilter is an example that enables the feature if the provided market value is among the configured markets. MarketFilterSettings is the filter settings type that matches the parameters of the filter config.
graph TD
CA[Client Application]
FGC["<b>IFeatureGatesConsolidator</b><br/>
• HTTP Context Integration<br/>
• Header Processing<br/>
• Feature Consolidation"]
FGS["<b>IFeatureGatesService</b><br/>
• Static & Experimental Features<br/>
• Override Handling<br/>
• Frontend Feature Processing"]
EFM["<b>IExtendedFeatureManager</b><br/>
• Configuration-based<br/>
• Query overrides<br/>
• Filter evaluation"]
EM["<b>IExperimentManager</b><br/>
• Dynamic experiments<br/>
• Customer targeting<br/>
• A/B testing"]
CA --> FGC
FGC --> FGS
FGS --> EFM
FGS --> EM
classDef default fill:#f9f9f9,stroke:#333,stroke-width:2px,color:#000
classDef client fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#000
classDef consolidator fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000
classDef service fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
classDef manager fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px,color:#000
class CA client
class FGC consolidator
class FGS service
class EFM,EM manager