Skip to main content

Features branching strategy using Git and Azure DevOps

Let’s talk about different branching strategies that can be used when you use Git. Let’s assume that you are working on a project where based on what clients need (and pay) you shall be able to provide a specific list of features. Each client can decide what feature he wants to buy and use.

The code is hosted inside Azure DevOps where you have a private Git repository. The solution is designed in such a way that you have a CORE layer, that it is common for all features. Because of the project complexity and how the solution is designed you cannot isolate each feature in a separate location. It means that the implementation of each feature can be found across the projects, in 2-3 different locations.
A good approach that you can have is to create a branch for your CORE layer. Once this layer is stable for each feature, you can create a new branch from the CORE layer.
The most significant advantage of this approach is the ability to merge different features at the moment when you need to build a specific solution for one of your clients. You can decide what the features that you want to include are.
Additional to this, on each feature branch, you can work without affecting the other features. Bugfix-ing and feature enhancements are done in an isolated environment, dedicated to that specific feature.
The complexity of the solution remains low by having a decomposition approach on top of Git branches.
The above approach is smart and keeps things simple, especially at a technical level. You don’t mix features between each other, and you can have at any time a clean solution with only the things that you need.
There is a moment in time when this approach adds extra complexity and generate an extra effort from a technical perspective. That moment is when the CORE is changed.

Any activity that is changing the CORE, from refactoring to a small change in the design will be required to push the change on each feature branch. When for example you do refactoring you might need also integrate the change to each feature, by updating how each feature is implemented or works. 
Even if you would have all the features in one branch, you would still need to do the fix; there is no doubt. The nice thing with this approach is that you can still use the previous version of the CORE combined with old versions of the features until you integrate the changes in all features. This is general for other approaches also, because you can create a branch each time when you have a stable version (or use tagging).

An interesting problem appears in the moment when you need to do a refactoring for example at the CORE. Because you have multiple branches, the technical team will have a hard time to estimate the real effort of the refactoring and the complexity level. They need to take each feature branch separately and analyze the effort. 
At this moment I think that the extra complexity that adds this approach is useful when inside your product you have a small number of features or variants that you want to separate. If you know from the beginning that you'll end up with 20 or 25 features this approach might not be the best one.

What should you do?
There is no solution that feets all cases. There are some things that I would recommend to take into account:
  • Understand the feature complexity and no. of features
  • Analyze the current approach and identify if each feature can be extracted in an isolated location (dedicated project)
  • Use a Dependency Injection mechanism that allows you to add at runtime features
  • Build each feature separately, isolated from the CORE
  • Decide if you deploy at the client all featured and you control from the license one features are active OR you deploy only the features included in the license
  • Consider using a package repository like Nexus and define clearly what a package contains and a packaging strategy
  • Analyze the solution complexity during the full life cycle of the application take this into consideration when you decide the feature strategy
Remember that there is no solution that fits all. Try to keep things as simple as possible for your current needs and at the same time you need a solution that is open to change in the future.

Comments

Popular posts from this blog

ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded

Today blog post will be started with the following error when running DB tests on the CI machine:
threw exception: System.InvalidOperationException: The Entity Framework provider type 'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer' registered in the application config file for the ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded. Make sure that the assembly-qualified name is used and that the assembly is available to the running application. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information. at System.Data.Entity.Infrastructure.DependencyResolution.ProviderServicesFactory.GetInstance(String providerTypeName, String providerInvariantName) This error happened only on the Continuous Integration machine. On the devs machines, everything has fine. The classic problem – on my machine it’s working. The CI has the following configuration:

TeamCity.NET 4.51EF 6.0.2VS2013
It seems that there …

Entity Framework (EF) TransactionScope vs Database.BeginTransaction

In today blog post we will talk a little about a new feature that is available on EF6+ related to Transactions.
Until now, when we had to use transaction we used ‘TransactionScope’. It works great and I would say that is something that is now in our blood.
using (var scope = new TransactionScope(TransactionScopeOption.Required)) { using (SqlConnection conn = new SqlConnection("...")) { conn.Open(); SqlCommand sqlCommand = new SqlCommand(); sqlCommand.Connection = conn; sqlCommand.CommandText = ... sqlCommand.ExecuteNonQuery(); ... } scope.Complete(); } Starting with EF6.0 we have a new way to work with transactions. The new approach is based on Database.BeginTransaction(), Database.Rollback(), Database.Commit(). Yes, no more TransactionScope.
In the followi…

GET call of REST API that contains '/'-slash character in the value of a parameter

Let’s assume that we have the following scenario: I have a public HTTP endpoint and I need to post some content using GET command. One of the parameters contains special characters like “\” and “/”. If the endpoint is an ApiController than you may have problems if you encode the parameter using the http encoder.
using (var httpClient = new HttpClient()) { httpClient.BaseAddress = baseUrl; Task<HttpResponseMessage> response = httpClient.GetAsync(string.Format("api/foo/{0}", "qwert/qwerqwer"))); response.Wait(); response.Result.EnsureSuccessStatusCode(); } One possible solution would be to encode the query parameter using UrlTokenEncode method of HttpServerUtility class and GetBytes method ofUTF8. In this way you would get the array of bytes of the parameter and encode them as a url token.
The following code show to you how you could write the encode and decode methods.
publ…