Reducing Magic Strings with Microsoft Feature Toggles (Microsoft.FeatureManagement)

This is the second part in a series.

One of the downsides of Microsoft’s feature flags/feature toggle implementation is the reliance on magic strings in code to query the status of features.

For example, the following code checks for the state of the Printing feature:

if (await _featureManager.IsEnabledAsync("Printing"))
{
    ViewData["PrintMessage"] = "On";
}
else
{
    ViewData["PrintMessage"] = "Off";
}

Notice in the preceding code that the feature is referenced with the string “Printing”. These kind of magic strings in code can be problematic because they have no sematic compiler meaning, for example if the feature is mentioned in multiple places and the name of the feature changes in configuration you would need to update all the strings in the codebase. It is easy to miss one and then end up with errors in the app.

In the documentation, the recommended approach is to define an enum containing the feature you want to control, for example:

public enum Features
{
    Printing,
    QuickQuotes,
    OnlineChat
}

Now the code can be refactored to make use of the enum in conjunction with a nameof expression:

if (await _featureManager.IsEnabledAsync(nameof(Features.Printing)))
{
    ViewData["PrintMessage"] = "On";
}
else
{
    ViewData["PrintMessage"] = "Off";
}

There are a number of things that can (and should) happen when using feature toggles/flags. One of which is that when a feature becomes permanent then the toggle and associated code should be removed. First this means removing it from configuration, which as I mentioned in part 1 will not actually break anything. The next step will be to remove it from the enum, doing this will actually break compilation (which is a good thing) if the enum value is referenced in a nameof expression. The next step is to fix compilation errors by removing the conditional code that relies on the flag/toggle.

In part 1 I mentioned the <feature> tag helper that can be used to enable/disable UI elements based on a flag:

<feature name="Printing">
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Print">Print Preview</a>
    </li>
</feature>

Notice in the preceding HTML that the name of the feature is still represented as a magic string “Printing”. If the enum value and config are removed, compilation will not beak if this HTML still exists. We can fix this by one again using nameof in the view:

<feature name="@nameof(Features.Printing)">
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Print">Print Preview</a>
    </li>
</feature>

Now if we removed Printing from the enum we get a compilation error and we won’t accidentally release an app to production with incorrect UI elements.

SHARE:

Comments (5) -

  • Thomas Levesque

    5/14/2020 6:52:42 PM | Reply

    I think an even better approach would be to add an extension method on IFeatureManager that accepts a Features value directly. This way you don't even need the nameof

    • Jason Roberts

      5/15/2020 2:59:17 AM | Reply

      Interesting idea Thomas Smile

  • Erik A. Brandstadmoen

    5/15/2020 9:13:27 AM | Reply

    This is good stuff. A small suggestion. When having the need for "string"-type enums (where you actually want a fixed set of strings), I prefer using static classes over enums. An example:

    public static class Features
        {
            public const string Printing = nameof(Printing);
            public const string QuickQuotes = nameof(QuickQuotes);
            public const string OnlineChat = nameof(OnlineChat);
        }

    this removed the need to use nameof(Features.OnlineChat) in usage, because Features.OnlineChat is already a string.

    • Jason Roberts

      5/18/2020 5:44:18 AM | Reply

      Hi Erik, that is true - the article references Microsoft guidance on this topic, however you could go one step simpler and remove the enum altogether and just has a static class with a set of public static readonly strings...

  • Jonny

    5/15/2020 6:45:06 PM | Reply

    Why not use enum.ToString()?

Pingbacks and trackbacks (1)+

Add comment

Loading