In the ninties, I was consulting a customer for a day. But in the end, I spent most of the day watching someone trying to compile the software. Today that’s no longer a problem. Even chaotic projects have a relatively current compiled version of the software available.
In a larger project I worked on, there were similar difficulties on another level: Each team built its own module. Before the release, regularly a task force was set up to integrate the modules of the different teams. Just getting all the modules to even compile together often took weeks. Today, in most projects, all version control changes by all teams are regularly checked out, compiled, and tested. As a result, such problems can no longer occur. The changes of all teams are constantly tested together. This is known as continuous integration: All changes made by all teams are continuously integrated.
Continuous integration servers such as Jenkins or TeamCity are tools to automate exactly this process. They check out changes to a particular branch from version control, compile the code, and run unit tests.
Feature branches are a pattern for using version control to separate the development of different features. Developers create new features in a version control branch. So each feature can be developed separately. With modern tools like Git, this is much easier than with many older tools. Git was originally started as a tool for Linux development. There branching is used to isolate the development of new features and also to develop several bug fixes in parallel separately. Only after a review the changes are actually integrated into the master branch.
Feature branches therefore isolate certain changes. And that’s exactly the contradiction to continuous integration: Changes from the different feature branches are no longer integrated. Only when the branch is transferred to the master branch, the changes from the branch are integrated with the other changes. And only then do conflicts and problems come to light. It is therefore no longer continuous integration. Instead, the integration occurs when the feature branches are merged.
One might argue that in some ways the work of developers between commits also contradicts continuous integration. Those who do not commit their changes for weeks will also have integration problems. In this respect, these are also branches. In practice, however, this hardly leads to problems, since frequent commits are an established best practice.
Branches can also exist for other reasons - for example for prototypes, spikes, or to try something out. Then the isolation of a branch can be better than changes going into the master branch that might have to be removed later.
Feature branches can work. Many teams have made good experiences with it. The branches should not exist for too long so that the merge conflicts do not become too big. Likewise, the number of branches should not become too large. Code should be exchanged regularly between the branches and code should be transferred from the master branch to the feature branches. But feature branches remain a contradiction to continuous integration: not all changes are integrated.
A continuous integration server for each feature branch does not help either. It can check whether the changes to the branch compile and also test them - but integration with the other branches is not done. However, it is possible to set up a continuous integration server that takes the changes from all feature branches and the master branch, integrates them, and then compiles them. This would actually be true continuous integration.
Continuous delivery exacerbates the problem: in addition to integration, compilation, and testing of continuous integration, further test stages are added to the pipeline until the software finally goes into production. Equipping each branch with a complete continuous delivery pipeline is not just time-consuming, but the pipeline must also end somewhere before production. After all, only one branch can go into production.
However, even with continuous integration, it must be possible to develop features without going into production right away. This is what feature toggles provides. This means that new features can be deactivated so that they cannot be used in production. These might be simple if statements. This allows new features to be developed without branches. The features are all implemented together in the master branch. In test environments, features can be enabled while they are still disabled in production.
Feature toggles also have other advantages: For example, bringing a new feature live is decoupled from the deployment. The software can be deployed, the feature remain deactivated, and then are then activated at a certain point in time. This makes it much easier to deal with deadlines for features. The software can be in production for a long time and at the decisive date only one feature toggle is activated.
Feature toggles are actually simple. But there are still problems: At some point outdated feature toggles have to be eliminated from the code - but when? And of course it has to be guaranteed that the right feature toggles are always on or off in production.
There are also other concepts : A/B testing, where one user group can use one feature and another cannot, is similar from a technical viewpoint, but serves a different purpose. Mixing these concepts makes the handling of the different configurations in test and production even more complicated.
Continuous integration contradicts feature branches. Continuous integration aims at continuous integration of all changes. Feature branches aim at isolation and decoupled development of features. However, this does not necessarily mean that one of the approaches is better. Anyone who works successfully with feature branches today and has no integration problems can continue to work with this approach. Continuous integration, feature branches, and many other concepts are in the end just tools from which teams choose their own personal toolbox. And feature toggles aren’t without challenges either.
PS: Thanks to INNOQ colleagues Philipp Haußleiter, Willem van Kerkhof, Andreas Krüger, and Philipp Schirmacher as well as my former INNOQ colleagues Alexander Heusingfeld and Timo Loist for discussing an early version of the blog post!