DEV Community

ohalay
ohalay

Posted on • Edited on

GitLab feature management for .Net

Motivation

Provide a mechanism for continued delivery together with continued development.

Solution

Use trunk-based development and feature toggles.

Implementation

Before starting to work with a feature, we need to create a new feature flag in GitLab and disable it. After that, all development should be branched with a feature toggle. When the feature is developed and deployed we can enable a feature in GitLab for the environment and it will activate during runtime. After the next release, we delete a feature flag and clean up the code.

GitLab feature management

GitLab provides a built-in solution for feature flags where we can create, add strategies, and manage. GitLab uses unleash client(we can also use direct GitLab API) to work with features. We can install unleash NuGet package from here.

Image description

During development, I found two weird things

  1. UnleashSettings has a specific property for Environment, but the client works only when we set the GitLab environment name to AppName.
  2. It is hard to understand where we should set Instance ID in UnleashSettings. In GitHub example - ProjectId, but property that work InstanceTag
var unleash = new DefaultUnleash(new UnleashSettings
{
   AppName = "environment_name" 
   UnleashApi = new Uri("API URL in GitLab config"),
   InstanceTag = "Instance ID in GitLab config",
   SendMetricsInterval = null,
});
Enter fullscreen mode Exit fullscreen mode

Environments

Important GitLab feature - environments. We deploy our solution to specific environments and we can activate/deactivate feature flags per environment.

Microsoft feature management

Unleash client is good enough, but we want to use
dotnet feature management, which has great integration with ASP.NET Core. To do that we should implement IFeatureDefinitionProvider for GitLab

internal class GitLabFeatureProvider : IFeatureDefinitionProvider
{
  private IUnleash unleash;
  public GitLabFeatureProvider(IUnleash unleash)
    => this.unleash = unleash;

  public async IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()
  {
    await Task.CompletedTask;
    foreach (var feature in this.unleash.FeatureToggles)
    {
        yield return CreateFeatureDefinition(feature.Name, feature.Enabled);
    }
  }

  public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
  {
    var active = unleash.IsEnabled(featureName);
    return Task.FromResult(CreateFeatureDefinition(featureName, active));
  }

  private static FeatureDefinition CreateFeatureDefinition(string name, bool active)
  {
    return new FeatureDefinition
    {
      Name = name,
      EnabledFor = active
        ? new[] { new FeatureFilterConfiguration { Name = "AlwaysOn" } }
        : Array.Empty<FeatureFilterConfiguration>()
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

Using feature management

In the end, we need to register feature management

.AddFeatureManagement()
.AddSingleton<IFeatureDefinitionProvider, GitLabFeatureProvider>();
Enter fullscreen mode Exit fullscreen mode

inject IFeatureManager into our solution and use it
await feature.IsEnabledAsync(nameof(FeatureFlags.myfeature))

Conclusions

  1. We manage our features during runtime using GitLab feature flags per environment
  2. We can rollback feature during runtime
  3. We develop and deploy without impact to the system and end users.

Help links

Top comments (0)