The purpose of continuous integration is to ensure that if a developer changes code in a software project, all the connected components and integration points are still in working order. In order to properly implement this idea, we must have automated regression testing in place that proves the application is in working order after a given change. Ideally, each change committed to the repository is tested so we can enjoy an immediate notification of failure before more changes are made on top of the existing code failure.
Initially, continuous integration is setup by triggering a build of the application’s source code, on each code commit. This is easily done with a variety of software applications available. The most popular free choice for this is Jenkins which is a Java based solution that will run on any platform. Jenkins will allow you to quickly make a project where you can define:
- The type and location of your source code repository.
- The command required to build your application.
- Send email notification on success or failure.
- The file mask of artifacts to archive on success.
Jenkins has the option to scan your repository on an interval to check for changes, if a change is detected a build is triggered, which will compile code, run any unit tests, code coverage, etc. This is the basic first step of continuous integration. The idea of ensuring a project is still build-able when code is changed is where we start in this process, but it is not nearly the end. In order to have a complete continuous integration solution, we must test the code end-to-end. This can be a time consuming process to create tests for every little thing, which begs the question: What will take more time, dealing with broken code, or investing the time in making tests ?
One common approach to this issue is to implement Test Driven Development or TDD within your development team. In short, the idea here is that for anything we program, we build the tests for the new functionality FIRST based on our user story requirements, then we get our tests to pass to prove that we are meeting these requirements. Then we refine our code to provide the complete desired functionality. In the end, we are left with unit tests that automatically become part of the build process. Unit tests are just another piece of the complete picture. The complete picture for continuous integration is as follows:
- Automated build triggered on code commit.
- Complete suite of unit tests run testing each part of the built code.
- A code coverage analysis tool that reports how much of your functions and classes are covered by tests.
- Source code analyzer to check for patterns that are known issues. Each programming language has their own type of tools for this, some are free, some are extremely expensive.
- A continuous deployment environment, which is used to automatically deploy your application to an environment that is a clean slate. A fantastic free tool to use to facilitate such a deployment to this and all environments would be Leroy. There are other expensive tools out there that will serve this purpose for you, but we made Leroy because we could not find any tool that did all the things we wanted it to do.
- Proper templatized configuration management with the configurations having their own unit testing to ensure its sanity.
- If the application has a database, using dbdeploy to wipe and build the database from scratch to prove there are no SQL errors, as well as ensuring there’s a proper test data set for automated testing.
- Post deployment testing to ensure the application is working properly on a basic level.
- Aggressive automated QA testing to test and ensure each defect and feature continues to work.
All of these bullets should be triggered / orchestrated from within your build system that you are using. Once the foundation has been properly laid for continuous integration, life is really good for the app team. This process is a fundamental component to an Agile development methodology and allows an application team to quickly change direction, make changes, add functionality, fix bugs and react at the speed of the business. Frankly, there’s no other viable approach to creating software. Common obstacles in setting up a process like this are:
- My build takes too long so we’re not seeing failures until it’s too late.
- I don’t have time to make tests.
- Building the database can not be automated because there’s too many errors and it’s a really old database that has been around for quite some time.
- We already have too much code written that doesn’t have any unit tests, and we don’t have time to circle back and make them.
- There’s configuration items hardcoded in our code that causes errors and breakage.
- All our developers develop database changes on a shared database and no one has their own database because it’s too much overhead to maintain that. Therefore, we are not properly capturing all the changes that all the developers are making.
These are common issues that come up. All of them have answers on how to solve them, but that is a bit outside the scope of this article. The reason I bring them up is to illustrate in broad strokes, what NOT to do. Solving these problems, and creating proper continuous integration environments, and automated deployments is what we do. It’s a complex and enjoyable process for us and there are a lot more details to delve into in this regard. We’d like to hear from our audience any feedback on this article. Taking constant feedback and input from all actors involved is part of a healthy agile process, which we enjoy participating and prospering from. We’d also like to hear about any problems or hardships that we may be able to help with. Please feel free to contact us at: [email protected] or follow us on Twitter.