Use feature flags in your plugins

Lately, there was a discussion on Twitter about the best practice to handle plugins: One class per plugin? One project per plugin? or anything else? On my side, I'm using one project for all the plugins for the same customer. This is what I answered on Twitter. Then came a question : How do I handle the fact that some plugins may have not been tested or completed yet and I need to release the plugins assembly. The answer was : Feature flag!

What is a feature flag? It's a concept that will allow to enable/disable some features or portion of code depending on a configuration.

Let's take an example where a plugin must execute two features : Feature A and Feature B
public class MyPlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        var service = new MyCustomService();
        service.ExecuteFeatureA();
        service.ExecuteFeatureB();
    }
}
We need to find a way to execute conditionally these methods, let's say because feature B is not tested or completed yet but we need to ship the assembly for the feature A.

What I generally use is the unsecure configuration of plugin steps to store a JSON that will explain what feature needs to be activated or not. Here is what the JSON looks like:
{"Features":[{"Enabled":true,"Name":"FeatureA"},{"Enabled":false,"Name":"FeatureB"}]}

So, in the constructor, I'm deserializing the configuration of the plugin step, so that I can use it easily in the Execute method. 

public class MyPlugin : IPlugin
{
    private readonly PluginFeature _feature;

    public MyPlugin(string unsecureConfiguration, string secureConfiguration)
    {
        if (!string.IsNullOrEmpty(unsecureConfiguration))
        {
            _feature =
                SerializerHelper.ReadObject<PluginFeature>(
                    new MemoryStream(Encoding.Default.GetBytes(unsecureConfiguration)));
        }
    }

    public void Execute(IServiceProvider serviceProvider)
    {
        var service = new MyCustomService();

        if (_feature.Features.FirstOrDefault(f => f.Name == "FeatureA")?.Enabled ?? true)
        {
            service.ExecuteFeatureA();
        }
        if (_feature.Features.FirstOrDefault(f => f.Name == "FeatureB")?.Enabled ?? true)
        {
            service.ExecuteFeatureB();
        }
    }
}
The benefit of this implementation is it does not require any query to find the flag. Because, yes, we could also use a custom entity or environment variables to store the same kind of configuration but that requires to query these data.

Of course, in a real world scenario, I'm using a base class that handles all this code automatically, so I just have to write the following:

public class MyPlugin : Plugin
{
    public MyPlugin(string unsecureConfiguration, string secureConfiguration) : base(unsecureConfiguration, secureConfiguration)
    {
    }

    public override void PostOperationCreate(CreationServiceProvider serviceProvider)
    {
        var service = new MyCustomService(serviceProvider);

        if (IsFeatureEnabled("FeatureA"))
        {
            service.ExecuteFeatureA();
        }
        if (IsFeatureEnabled("FeatureB"))
        {
            service.ExecuteFeatureB();
        }
    }
}

Comments

Popular posts from this blog

New XrmToolBox plugin : Import/Export NN relationships

Searchable Propery Attribute Updater

Full featured CRM grid in HTML/JS