The term “continuous integration,” in its simplest form, indicates that all developers on a team should commit their code changes to the code repository on a frequent basis (at least once a day). This will naturally lead to multiple team-level integrations per day. This frequent, small-content integration reduces the risk and cost of team-level code integration efforts. That alone is a great reason to take up this practice. In this blog post, we will discuss this reason, as well as, the general methods to implement continuous integration (CI).
CI by Hand
There are different levels and aspects of CI. If you aren’t practicing CI at all and have never had the luxury of being able to procure continuous build server software, I’ve got good news for you. The software can be had for the low price of FREE! Until that procurement happens, you can adopt the practice in a completely manual manner.
Choose a single team member to be your “code custodian”
At an appointed time every day, the team stops coding for a moment. The code custodian gets the latest version of the source code from the repository and performs a manual build.
The code custodian reports the results of the compilation. If it is successful, the team resumes coding secure in the knowledge that their code at least compiles as a single solution. If the compilation fails, the team works together to “fix the build,” doing whatever is necessary to integrate the code so the entire solution compiles successfully. A broken build is a show-stopper; the entire team should stop working on anything else* until the broken build is handled or fixed.
* Broken builds are like disturbed anthills. When the anthill is disturbed, all the ants stop whatever they are doing to start addressing the disturbance. When a build breaks, the entire team should stop doing anything other than attempting to fix the build. It could be that work is simple, but since it is still work, it seems appropriate to say, “Stop working on anything else.”
The approach to a single large integration prior to a production push typically requires a Herculean effort. The key point to remember about CI is that the effort to integrate code on a day-to-day basis is low, especially relative to the single large integration. In addition, the time pressure inherent in integrating immediately prior to a production migration is far more stressful and unhealthy for everyone involved. Thanks to the daily integration effort, these pressures and stresses can easily be avoided.
Implementing CI by hand will immediately lower integration effort for the team. However, the full benefit of CI are realized best when the team procures a continuous integration/build server. There are many excellent products to choose from in the marketplace. I have used CruiseControl.NET, TeamCity, Bamboo, and TFS. The great thing about all of these software packages is that despite the configuration differences, the workflows are all very similar. For the remainder of this blog, I will be using a local installation of TeamCity and a remote Subversion repository. However, I will not be covering the installation of the server software. If you are unfamiliar with this type of software installation, be sure to watch the video. We will be dealing with actual TeamCity build configurations throughout the rest of this blog.
NOTE: TeamCity is free for up to 20 different build configurations and Subversion is also free. Cost should NOT be a deterrent to implementing these items into your project. If you’d like to see the “How-To” of CI with TFS Cloud services, let us know via the comments section or by tweeting @CrederaMSFT.
Replace the Custodian
After the CI server is installed (including having whatever client notification tools are provided on each team member’s machine), it’s time to configure it to take over the code custodian duties. The CI server will have the ability to perform operations on a schedule. For our purposes, we want the server to get the latest code from the source code repository and compile it in the same time the custodian was doing it. I prefer doing this first thing in the work day, if possible, since broken builds can be dealt with in a timely and fresh-minded manner. “Nightly” builds are also perfectly acceptable as long as the rest of the process supports and manages expectations appropriately.
Let’s assume our work day begins at 8:30 a.m., in which case I would schedule the daily build for 9:00 a.m. In TeamCity, you set up a build as follows (assuming you already have a project defined):
1. Set up a new build configuration for the project. This configuration will handle the daily build.
2. Attach a version control system (VCS) to the build configuration. Recall that ours is Subversion.
The rest of the settings on this screen can be left at the default values.
3. Be sure to test the connection prior to proceeding to make sure you’ve entered all of the credential information correctly.
4. Finally, you will add a build step. This is where the action is! Since we’re doing a .NET project, our runner will be MSBuild, and the build file path will point to the solution file.
5. At this point, it makes sense to verify that the build works. Click the “Run” button for your project from the main “Projects” page.
If everything has been configured correctly, you should get a “Success” message.
6. Since we know the build works, it’s time to add the schedule. We want this build to happen automatically at 9:00 a.m. every day. This is easily accomplished by editing the build configuration and adding a build trigger.
7. Select a Schedule trigger and set it for 9:00 a.m. daily.
At this point, you have an automated daily build which will trigger at 9:00 a.m. every day. The build will fail if the solution fails to compile and the team will be able to take appropriate action. However, we can do even better!
Build on Every Commit
Once the team is in the groove with daily builds, it is time to introduce builds on every commit. Every time a team member commits something to the code repository, a build should be kicked off to verify that the new code plays well with the rest of the system. This allows for more immediate feedback regarding the viability of the changes the team member just made with respect to the rest of the solution.
Many CI servers are implemented such that “build on every commit” is actually “check the source code repository every x number of minutes for changes.” In practice, this works out fairly well. If a team member wants immediate feedback, but doesn’t want to wait for the polling period to expire, the CI server typically exposes the ability to run the build immediately (on demand).
To set up a build on every commit in TeamCity, we can either extend/alter the daily build configuration we already have or create a new configuration altogether. For simplicity, I will alter the above configuration (and change its name appropriately). The only mandatory step is to include a VCS trigger.
Once this step is saved, the build server will start performing builds as soon as it detects a change in the source repository. One thing to note here is that the VCS associated with this configuration is set to poll for changes every 60 seconds. This will result in at most a one minute delay between the time you commit a change and the time the build server picks up the change.
Transform your business operations with our Microsoft solutions
Explore Our Microsoft Consulting Services →
Extend the Build with Tests
What about those automated tests we developed after reading the TDD post? Now that the team is sure the solution is being compiled every time they commit changes to the source code repository, tests can be added to the build. This is where some of the greatest value of CI can be exhibited. After adding tests to the build, the team will not only be able to validate compilation of the solution, but they will also be able to validate that the solution is behaving as intended. Even without CI, team members should be running the entire test suite prior to committing code to the repository.
Adding the tests to the build typically requires adding just one more step to the workflow. The step executes a test runner of some kind against a list of assemblies that contain the tests. This is another good reason to have your tests self-contained (in a separate assembly).
TeamCity has a simple way to do this. All we have to do is add another build step to our existing build configuration to execute our tests. This one should use the NUnit test runner since our tests are coded using NUnit.
After adding this step, run the build by hand to make sure it’s configured correctly (and that your tests are all passing). You now have a CI build that compiles and runs automated tests every time a team member commits code to the repository. This will help ensure that business rules don’t get broken moving forward and when they are broken, the entire team knows about it immediately.
CI is a practice that can be done by hand by any team. Most teams will benefit from automating the activities by investing in CI server software and developing practices on the team that maximizes its benefits. This blog post is not comprehensive regarding the benefits and capabilities of a CI server. I’ll leave that for you to dig into and find out more (Hint: “pre-tested commit”). If you have questions or comments about the content already provided, be sure to leave us a comment below or send a tweet to @CrederaMSFT.
Check out Part I and Part II if you missed them. Next in the Iterating Towards Excellence series will be a post on “Collective Code Ownership.” Check back soon for more!