Software teams normally consist of several developers. They work independent or in small groups on their own tasks. And of course, sometimes the developed code has to be merged together to get a running application. One kind of merging principle is known as “Continuous Integration”. It is a concept to merge all work copies to a shared source. This merging is done “often” which may be one or several times a day depending on the size of the tasks to implement.
But why should you merge that often? Does it give you any benefits? Wouldn’t it be easier if everyone implements his own tasks without any interruption and you merge everything together right in front of a release? Within this article I want to show the benefits of Continuous Integration, name some pre-conditions, address issues and think about possible ways to implement this concept.
Continuous Integration will create following benefits for the developers:
- Frequent backup of the code within the source control will allow going back to a previous version without losing much work. For example in case the new code generates errors it might be easier to throw the new code away and start over again than to find and solve difficult to find errors.
- Collective work on the code base will be much easier as the whole team will always work with up to date source code.
- Frequent integration of small parts reduces the merging effort.
- Problems regarding the integration of new components are found early.
- Errors regarding the interaction of components are found early.
- Integration issues are constantly discovered and corrected – not just before a milestone.
- At any time – maybe daily – a running product is created which can be used for further integration test and system test or which may be delivered to the customer for test purposes, to show now features and to get a first customer feedback.
And of course Continuous Integration brings multiple benefits to your organization:
- Increase visibility which enables better communication.
- Issues will be found fast and can be solved with low effort.
- Spend less time debugging and more time adding features.
- Reduced integration problems allowing you to deliver software more rapidly.
Continuous Integration is cheap. As you will see in the next chapters the pre-conditions can be fulfilled easily and the workload for the frequent code merging is very low.
Not continuously integrating is costly. If you don’t follow a continuous approach, you’ll have longer periods between integrations. This makes it exponentially more difficult and therefore more expensive to find and fix problems. Such integration problems can easily knock a project off-schedule and you may fail deadlines for milestones.
An often asked question is whether Continuous Integration will reduce the number of bugs and increase you software quality.
Martin Fowler has answered this well: “Continuous Integration doesn’t get rid of bugs, but it does make them dramatically easier to find and remove.”
To get the Continuous Integration process running, you should set up following:
- The team uses a common code base which is managed in a version control system.
- No code without tests. Prior to, during or latest directly after feature implementation the test cases must be implemented. Productive code and related tests must be checked in together.
- An automatic build is created during check-in of the code.
- The build process must be fast.
- The tests must be executed fast. In case you have long running integration tests you should split up the different test types into fast module tests and long running integration test.
- The tests will be executed in a test environment which is a clone of the productive system.
- At the end of the tests an automatic summary or report is generated.
- Make it easy to get the latest deliverables. The results of the build process must be accessible in an easy way.
Issues during code integration
Of course, a code check-in may create issues:
- The changed module contains errors and some tests fail.
- The changed module leads to error in dependent modules and their tests will fail.
- The module interface was changed and therefore the dependent modules must be changed too. In this case the build will break.
Therefore a typical integration process within a group of developers will contain the following steps:
- Check-in of the changed module.
- Execute module tests.
- Adapt dependent modules in case of interface changes.
- Execute module tests of dependent modules.
- Execute integration tests.
Until all of these steps are executed the source control system contains erroneous code or even code which will break the build. Therefore one claim in the Continuous Integration theory is that the team will fix such issues immediately. Of course, this will not work in reality. The team gets inefficient if it often has to stop work, fix issues and return to the previously executed work. Such task switches are very time consuming.
One solution to reduce this pain is a good synchronization and communication between the team members. But I think a better solution is to prevent these issues automatically. Most parts of the Continuous Integration process are so simple because their where done by ore supported with automated tasks. So it’s not surprising that we have such helping server tasks for the check-in too, the so called “gated check-in”.
Gated check-in is a form of Continuous Integration. But it is stricter. It will verify the code during the check-in process and reject the code if an error occurs. During gated check-in a build will be created and all module tests will be executed. If the build breaks or a test fails, the changes will be rejected. This strict way of Continuous Integration has the bi advantage of protecting the team against work interruptions due to mistakes of one team member.
This sounds fine but there is one major issue. As seen before there are situation where you have to check-in a module with a changed interface and another team member has to adapt his module accordingly. In this case you explicitly want to commit code even if you know that it will break the build.
Combine both ideas
Continuous Integration without gated check-in has the advantage of flexibility. Gated check-in adds the benefit of automated validation to provide unnecessary team interruptions. Therefore, in my opinion you should combine both concepts.
I prefer gated check-ins with the possibility to disable the validation of the committed code. I think the bigger number of code commits is done without interface changes. And even if there are interface changes, the dependent module(s) where adapted by the same person which has changed the interface. In these cases there is no need to commit code which breaks the build ore the tests. So a gated check-in is the best solution. And for the minor number of cases where an interface change shall be submitted as a team member is responsible for the adaptation of his modules, you shall allow to commit without validation. In this case the synchronization between the developers must be done within the team.
In case you have an implementation process with a lot of interface changes and a scattered module responsibility you may often run into the situation where you have to check-in without validation. In this case you can use different code branches. You may create a working branch to check-in all changes without validation. And you may have a main branch with the last valid code. In case implementation on the working branch is finished it can be merged with the main branch. And this merge should be done by using a gated check-in to ensure a stable build for this branch.
Continuous Integration allows to the developer check-in its code without having the uncertainty whether it will be accepted or not. Important is that the developer will always have to be confronted as soon as possible with negative results of a check-in. It’s is an optimistic principle trusting the developer or development team to fix these issues quickly. If this isn’t working well, you can add a good branching model or add validation rules for the check-in (gated check-in).