Continuous Integration: An Overview
Continuous Integration at Grio
In the same way that testing code is more and more becoming table stakes for modern software projects, continuous integration is increasingly becoming standard on projects of any non-trivial degree of complexity. Here at Grio, we leverage continuous integration whenever possible, in order to eliminate time spent performing routine deployments and tracking down integration issues.
What Is Continuous Integration?
Strictly speaking, continuous integration is simply the practice of merging developers’ working copies of code into a project’s mainline at regular intervals during the project. In this strictest sense, it seems almost impossible to imagine not using continuous integration. When this term was coined, it was not uncommon for multiple developers to work in isolation, building components to a specification, uninterrupted for relatively long periods of time. Once done, the team would perform one big, messy integration. However, continuous integration in modern practice has come to be synonymous with and include a number of best practices related to automated testing and continuous deployment as follows:
- Maintain a code repository
- Automate the build
- Make the build self-testing
- Everyone commits to master every day
- Every commit to master should be built
- Keep builds fast
- Test in a clone of production
- Make it easy to get the latest deliverables
- Everyone can see build outcome
- Automate deployment
From the Wikipedia article on the subject.
Related Tools and Practices
There are some useful tools and practices that support the list above (meta best practices, if you will), that should be treated as indispensable when setting up a new project:
Component Based Architecture, SOA, etc.
Architecting your project using a Service Oriented Architecture, or at least breaking up your primary architecture into smaller, self-contained components, will help to ensure the build step remains fast. Rather than “maintain a code repository”, one might instead suggest to “maintain a code repository per component”. For example, a quick modification that will encourage testing on the average web project is building a client application that consumes the back-end data via API. In this way, the best-in-class tools can be used for testing on either side of the client-server relationship.
If you’re writing in Ruby, use Guard with
guard-rspec. For Node.js, the Mocha framework has a built-in watch mode (using the flag
-w). For client-side work, use
grunt-contrib-watch for Grunt (or
gulp-watch for Gulp if you’re into the really bleeding-edge). Adding a watch utility that can run your tests goes a long way toward ensuring developers are actually running the tests during their local development. This, in turn, helps to ensure less bug-fixing is needed when a feature is pushed up to an integration branch and tested on a centralized build server.
A build server should be configured in such a way as to automatically pull the latest code, and then compile and test the latest build (or possibly just test it in the case of an interpreted/scripting language). Travis and Jenkins are two open source continuous integration server systems. Setting up either tool on a private server requires a reasonable amount of configuration, prompting a handful of companies to offer Continuous Integration as a Service (CIaaS) platforms. Travis, CircleCI, and Codeship are just a handful of companies in this domain. Grio has used Travis to some degree of success, and will be exploring CircleCI for future projects.
One of the major advantages of using a build server is the ability to set up continuous deployment at a single “endpoint”. Instead of individual developers needing to manage deployments, the build server is configured once and used to automatically deploy to any number of environments after a successful build. Any of the CIaaS providers mentioned above support the ability to selectively deploy specific branches to specific environments, in the case where manual QA or some other review process is needed for production deployments.
Getting started with continous integration is made considerably easier by starting right. Some factors that should be considered are as follows:
Pick the Right Framework
Modern web development frameworks such as Rails, Grails, Django, etc. are either integrated with a testing framework by default, or geared toward testing or test-driven development. Getting this configuration out of the box can encourage good testing and code hygiene throughout a project’s lifetime.
Add Basic Tests
If constraints prevent full test coverage, focus on a suite of end-to-end “health checks”; these should cover critical functionality from the perspective of the user. Putting this in place can help catch regressions during routine development and staging deployments, thereby supporting QA efforts. Furthermore, this will help prevent the automated test suite from becoming stale, which can encourage the addition of more in-depth tests as time permits.
Hosting and CIaaS
During discovery, it can be helpful (especially for application prototyping) to search for managed hosting that is geared toward continuous integration. For example, both Heroku and AWS’ OpsWorks support single-step, API-based deployments. In coordination with a CIaaS platform, this can reduce the problem of running full continous integration to one of configuration, rather than implementation. With the addition of a YML or JSON configuration file or two, an entire integration/deployment process can be put in place.
The pros of using continuous integration and deployment on a software project are many. From the perspective of shareholders and non-technical teammates, continuous integration and deployment can provide immediate insight and encourage collaboration. For web products specifically, this means that designers, project managers, etc. can see the latest build almost immediately at a URL with little or no intervention from development.
From the perspective of testing and development teams, the benefits are obvious. Testing is encouraged, as it supports confidence in automated deployments. Developers can free up time spent performing routine internal deployments, and, in the ideal case, free up time from deployment period. As such, it also encourages approaches and implementations that lend themselves to repeatable deployments, which can reduce deployment issues over time.