DEV Community

Cover image for Don't break production when you add new features! - Feature Toggles in C#
Rogelio Gámez
Rogelio Gámez

Posted on

Don't break production when you add new features! - Feature Toggles in C#

Problem statement: You broke production after you added a new feature ):

You just added a new feature to your application, it passed all the unit, integration, and E2E testing. It was released and for two weeks, you haven't heard anything bad about it. But now, customers are having a problem with a process that has been working for 5 years without issues. After some digging, you discover that the feature that you added, broke the application if this or that specific state is reached.

What can you do? A fix would take at least 2 to 3 days to implement and release, but your application is practically unusable for the time being.

Proposed solution: Feature Toggles

Feature Toggles or Feature Flags is one of many ways to release new features.

Feature flags (also commonly known as feature toggles) is a software engineering technique that turns select functionality on and off during runtime, without deploying new code. [1]

In code, they look like this:

public void VeryImportantMethod(IFeatureManager featureManager)
{
    if (featureManager.IsEnabled(FeatureToggles.YourNewCoolFeature))
    {
        ExecuteNewFeature();
    }

    ExecuteExistingFunctionality();
}
Enter fullscreen mode Exit fullscreen mode

We have a feature manager dependency that will handle whether the feature is enabled or not. The feature manager can take this configuration from settings files, databases, services, etc.

If your feature needs to be disabled because it breaks existing functionality. You would only need to modify it from your feature toggles source.

One example of a feature toggle source is the Feature Manager in Azure App Configuration:

edit-feature-flag-expanded
Image taken from Microsoft Documentation [2].

Downside of Feature Toggles

There is a big downside to using feature toggles: complexity. [3]

When you add a new feature toggle, you are branching your application execution into a new path that you will need to take into account when you are debugging. And it becomes even more complex when there are feature toggles within feature toggles.

Feature Toggles good practices

The feature toggle manager should not be a concrete object

This goes for all your dependencies in your codebase. The Dependency Inversion Principle states that we should depend on abstractions, not concretions. And feature toggles are not the exception.

Ideally, you should have a static class (or similar depending on your language) with a list of all the feature toggles in your application.

public static class FeatureToggles
{
    public const FeatureA;
    public const FeatureB;
    // ...
}
Enter fullscreen mode Exit fullscreen mode

And for your feature manager, you need an interface like:

public interface IFeatureManager
{
    bool IsEnabled(string featureToggle);
}
Enter fullscreen mode Exit fullscreen mode

Then you can inject it into your code:

public class CoolClass
{
    private readonly IFeatureManager _featureManager;

    public CoolClass(IFeatureManager featureManager)
    {
        _featureManager = featureManager;
    }

    public void CoolMethod()
    {
        if (_featureManager.IsEnabled(FeatureToggles.FeatureA))
        {
            ExecuteFeatureA();
        }

        ExecuteExistingFunctionality();
    }

    // ...
}
Enter fullscreen mode Exit fullscreen mode

This way when you create unit tests, you can mock IFeatureManager easily to test your new feature.

You can also implement another IFeaturesManager that takes the feature toggles values from another source without affecting existing code.

In your unit tests, do not check if the feature toggle is enabled or not, mock it!

It is easy to fall into this trap while testing feature toggles:

[TestMethod]
public void TestCoolClass()
{
    // Arrange
    IFeatureManager featureManager = new ConcreteFeatureManager();
    CoolClass coolClass = new CoolClass(featureManager);

    // Act
    coolClass.CoolMethod();

    // Assert
    if (featureManager.IsEnabled(FeatureToggles.FeatureA))
    {
        // Assert FeatureA works
    }
    else
    {
        // Assert existing functionality without FeatureA
    }
}
Enter fullscreen mode Exit fullscreen mode

This is an anti-pattern in unit testing, we should not have branching logic. Instead, mock the feature manager interface and test both behaviors separately.

Example using Moq (https://github.com/moq/moq4):

[TestMethod]
public void TestFeatureA()
{
    // Arrange
    Mock<IFeatureManager> featureManagerMock = new Mock<IFeatureManager>();
    /// We make sure the feature manager mock returns true when FeatureA is requested.
    featureManagerMock
        .Setup(fm => fm.IsEnabled(FeatureToggles.FeatureA))
        .Returns(true);
    /// We get the mocked interface to pass as dependency.
    IFeatureManager featureManager = featureManagerMock.Object;
    CoolClass coolClass = new CoolClass(featureManager);

    // Act
    coolClass.CoolMethod();

    // Assert FeatureA works as expected
}

[TestMethod]
public void TestExistingFunctionality()
{
    // Arrange
    Mock<IFeatureManager> featureManagerMock = new Mock<IFeatureManager>();
    /// We make sure the feature manager mock returns false when FeatureA is requested.
    featureManagerMock
        .Setup(fm => fm.IsEnabled(FeatureToggles.FeatureA))
        .Returns(false);
    /// We get the mocked interface to pass as dependency.
    IFeatureManager featureManager = featureManagerMock.Object;
    CoolClass coolClass = new CoolClass(featureManager);

    // Act
    coolClass.CoolMethod();

    // Assert existing functionality works as expected
}
Enter fullscreen mode Exit fullscreen mode

Have a formal approach to deal with feature toggles

Feature toggles are not meant to be eternal. Once your feature is well tested even in production, you will need to remove the feature toggle from your code, which is sometimes not that straightforward. Your team needs to have a formal approach to remove feature toggles once the feature becomes stable enough.

References

  1. Buchannan, I. (n.d.). Feature flags. Atlassian. Retrieved February 24, 2023, from https://www.atlassian.com/continuous-delivery/principles/feature-flags
  2. Maud-Lv. (n.d.). Tutorial: Use azure app configuration to manage feature flags - azure app configuration. Tutorial: Use Azure App Configuration to manage feature flags - Azure App Configuration | Microsoft Learn. Retrieved February 24, 2023, from https://learn.microsoft.com/en-us/azure/azure-app-configuration/manage-feature-flags
  3. Fowler, M. (n.d.). Feature toggles (aka feature flags). martinfowler.com. Retrieved February 24, 2023, from https://martinfowler.com/articles/feature-toggles.html
  4. Maud-Lv, M., et al. (n.d.). Tutorial for using feature flags in a .NET core app. Tutorial for using feature flags in a .NET Core app | Microsoft Learn. Retrieved February 24, 2023, from https://learn.microsoft.com/en-us/azure/azure-app-configuration/use-feature-flags-dotnet-core?tabs=core5x
  5. Farley, D. (2021, January 6). Continuous integration vs feature branch workflow. YouTube. Retrieved February 24, 2023, from https://www.youtube.com/watch?v=v4Ijkq6Myfc

Top comments (0)