Stravaig.FeatureFlags

An extension to Microsoft.FeatureManagement based on Code That Fits in Your Head

Getting Started

To start using Stravaig.FeatureFlags in your solution, first add the Stravaig.FeatureFlags Nuget package to the project where you want to set up the feature flags. This should be a fairly central location as they will likely need to be accessible from anywhere in your application. It will also have to be accessible from the project that sets up the dependency injection.

Create a enum that represents each of your feature flags. For example,

1
2
3
4
5
6
public enum ApplicationFeatures
{
    SendEmail,
    SendSms,
    NewShippingCalculation,
}

Add the [StronglyTypedFeatureFlags] attribute to the enum, you’ll have to add using Stravaig.FeatureFlags as well. So the code will now look like this:

1
2
3
4
5
6
7
8
9
10
11
using Stravaig.FeatureFlags;

namespace MyExampleApplication;

[StronglyTypedFeatureFlags]
public enum ApplicationFeatures
{
    SendEmail,
    SendSms,
    NewShippingCalculation,
}

Finally, add the feature flags to the service container in the Microsoft Dependency Injection container, like this:

1
2
3
4
5
6
7
8
9
10
11
using Microsoft.FeatureManagement;
using Stravaig.FeatureFlags;

// services is your IServiceCollection.
// Configuration is your IConfiguration.
// ApplicationFeatures is your enum. (See above)
services.AddFeatureManagement(Configuration)
    .SetupStronglyTypedFeatures(opts =>
    {
        opts.Add<ApplicationFeatures>();
    });

You can add as many enums as you like.

Using the feature flag

Each enum value will translate into a class and interface that is injected into the dependency injection container. Therefore, anywhere you can inject into a class, you can inject a feature flag.

The feature flag interface will have the name I<name>FeatureFlag where <name> is the value name in the enum. So, for example your class might be set up like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace MyExampleApplication.Services;

public class EmailService
{
    private readonly ISmtpClient _smtpClient;
    private readonly ISendEmailFeatureFlag _sendEmail;

    public EmailService(ISmtpClient client, ISendEmailFeatureFlag sendEmail)
    {
        _smptClient = smtpClient;
        _sendEmail = sendEmail;
    }

    public Task SendEmailAsync(string subject, string recipient, string body)
    {
        if (!await _sendEmail.IsEnabledAsync())
            return; // Feature is off, do nothing.
        
        // Do stuff for sending email.
    }
}

Setting lifetimes

Each feature flag can have its own lifetime, or all feature flags can have the same lifetime, or you can set a default lifetime and override it on a case by case basis.

Say your SendEmail and SendSms feature flags are in place to prevent emails and SMS messages being sent from development or testing environments, you might want to set them with a Singleton lifetime as they won’t change during the lifetime of your application. However, your NewShippingCalculation is to be applied at a specific point in time you could set that to Transient or Scoped.

To set the default lifetime, you add a property to the [StronglyTypedFeatureFlag] attribute on the enum. If you don’t set it, the default will be Scoped.

1
2
3
4
5
[StronglyTypedFeatureFlags(DefaultLifetime = Lifetime.Singleton)]
public enum ApplicationFeatures
{
    // ...
}

If you want to set the lifetime on a case-by-case basis then you can set it on each individual value, like this:

1
2
3
4
5
6
7
8
9
10
11
12
[StronglyTypedFeatureFlags]
public enum ApplicationFeatures
{
    [Lifetime(Lifetime.Singleton)]
    SendEmail,

    [Lifetime(Lifetime.Singleton)]
    SendSms,

    [Lifetime(Lifetime.Scoped)]
    NewShippingCalculation,
}

You can also mix the two styles. e.g. If most of your flags are one lifetime, but some are different:

1
2
3
4
5
6
7
8
9
[StronglyTypedFeatureFlags(DefaultLifetime = Lifetime.Singleton)]
public enum ApplicationFeatures
{
    SendEmail,
    SendSms,

    [Lifetime(Lifetime.Scoped)]
    NewShippingCalculation,
}

Naming your Feature Flags

If you name the the values of the enum something like MyFeatureFlag then the generated interfaces and classes will have redundant parts to their names as they have FeatureFlag appended. So if you do that you’ll be injecting an interface called IMyFeatureFlagFeatureFlag.

If names clash because you have multiple enums set up and they have duplicate names then this can manifest in a couple of ways.

  1. If the enums are in different namespaces then you’ll get multiple classes and interfaces that resolve to the same feature. However they may end up with different lifetimes and values which could cause confusion.
  2. If the enums are in the same namespace then you’ll get a compilation error as duplicate classes and interfaces will be created.