Why and how to avoid branches 2


Share it!Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInEmail this to someoneShare on Reddit

There are many branching models. In most cases their intention is clear – to separate development into several streams. Commonly mentioned benefits include faster implementation of new features due to a lack of interference with other changes, increased stability of particular branches, controlled work publishing to a baseline, easier identification of which changes broke which particular test or area of application.

Unfortunately, branches that require them to be merged at some point in time introduce a major problem – their integration to a baseline. You have probably participated in a horror merge more than once. I was personally participating in a merge that took more than 2 months to complete (and there were several people involved). Moreover, many such stories end in a huge amount of regression in a trunk/master which results in another days, weeks or even months of bugs fixing (that we, of course, very much enjoy!).

This approach doesn’t also go in pair with Continuous Integration practice. We would like to always have a stable and releasable baseline which integrates daily all changes from the different development streams. It is also a prerequisite for introducing Continuous Delivery. Unfortunately, it means that all developed code should be committed on a daily basis, whether the new feature is fully implemented or not. To achieve it, we can follow one of four ways: hide new functionality until it is ready, perform incremental changes, use branch by abstraction pattern or componentize your application.

Lets start with the most obvious path – perform incremental changes. While this way is not always  applicable it is possible to follow it with some additional effort in most of the cases. It often requires additional analysis and a little more code that can be cleaned afterwards. Coding this way often leads to dividing requirements into smaller tasks and results in a better understanding of the topic by the developers. However, as mentioned above, there are changes that cannot be handled this way.

Another way of dealing with incremental changes is to hide new functionality until it is finished. The main idea is that features can be turned on and off by a property or some kind of license mechanism. There is some architecture impact and additional complexity to the system but gains are significant – all already written code for all unfinished features is integrated with a baseline. Moreover, sometimes blocking a new feature can be as simple as blocking access to a URL under which new functionality will be available.

switch

Picture by g4ll4is, on Creative Commons

The third way, branch by abstraction, is a little bit more complicated. It is divided into two major steps:

  • refactor the system in a way that current and new functionality will be plugable. As a result, you should end up with an abstraction layer over the current code and current code using this abstraction layer,
  • write new functionality in a way that it will be able to replace the current one without changing the abstraction layer.

Then, either programmatically or by some configuration you will be able to easily switch old implementation to a new one. This way is the most complicated out of the three presented up to now and might require a lot of additional effort. On the other hand, such refactoring usually improves design significantly. Moreover, the abstraction layer can be removed afterwards if there are strong assumptions that it will not be required in the future or overcomplicates a system.

The next way to deal with branches is to componentize your system. The topic is so broad that dozens of books have been written about it already. To just scratch the surface, the components allows you to decouple parts of your application which are highly independent on each other, have different lifecycles or change at different rates. Unfortunately, managing dependencies between components sometimes requires a significant amount of effort. Moreover, all Continuous Integration and Continuous Delivery processes become more complicated and potentially more fragile.

Everyday integration of all changes related to your system provides tremendous value. Without it, even if you think that you are using Continuous Integration methodology, you lose most of the benefits that it provides. Properly implemented it allows you to develop pretty fluently and without hitches. Feature or refactoring branches looks promising. They are very tempting and many organizations are not able to resist. However, the whole gain of uninterrupted development is usually lost during integration with the baseline phase. The more simultaneous development happens the bigger the problem is during a merge. Thus it is worth it to avoid branches as much as possible. With the 4 above strategies we can do in almost all cases.

Share it!Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInEmail this to someoneShare on Reddit
  • Michal Pietrus

    Thanks for such branches overview. I think the topic of this post is quite misleading, because the problem is not with branches itself, but perhaps with the process, or careless decisions made in the past.
    Actually, it even does not have to be a careless decision as the main product of company Foo might have been branched X time ago (eg. because various set of features for different customers) and now it seems it would be great to have it merged in master (eg. because these customers are having now the same set of features). In this case this is mostly business impact, but development has to handle that. Any VCS with all of its features is just another tool to support business operations and there are always edge cases.

    • http://continuousdev.com/ Piotr Oktaba

      I think it was a bad decision to not develop new features for different customers in master. It happens quite frequently that after a year or two business decides to merge such changes to a baseline what results in a horror merge which takes months. IMHO, it would take much less effort to create some kind of license mechanism and develop from the very beginning in baseline.

      Regarding VCS, IMHO they are not very helpful in such situations. While of course GIT handles merges much more better than SVN, majority of problems comes from code incompatibilities under such circumstances.