Many times, development teams forget that the process of software development does not end with code compilation, systems integration and successful testing. To actually have value to the business, code must be delivered into the arms of those waiting for the work to occur – either internally or externally. This tip will tell us how by using continuous integration servers, we can automate our release processes all the way from code check-in to deployment.
Why not take the next step to continuous deployment?
The Agile Manifesto says we value “working software over comprehensive documentation.” Of course, the rub comes in when we try to get to that deployment of working software. We think that the deployment should be easy. After all, the hard work was done already in writing the code. But, as Yogi Berra said it, “In theory, there is no difference between theory and practice, but in practice there is.”
We use continuous integration servers to help us maintain consistent high quality code all the time. By always building and testing our code every time we check-in code on the continuous integration server, we can always make sure that we never regress our code by now having a new feature or fix clobber old code that used to function. And that’s a good thing! In Lean terms, we are making sure that the Work In Progress is kept to the barest minimum consistent with good practice. We save ourselves time and confusion when it comes to integration and testing by maintaining a constantly working system – one that is always “ready to ship.”
So why is it that we so often stop with development, and don’t go the extra step and have a continuous deployment system as a logical extension of the CI server? That’s a tough question. If you look at many organizations, there is a “gate” that one has to stop at before the code gets deployed; one where a toll is paid, in terms of documentation and/or ceremony. A gate where a hand-off happens, and the cherished code is taken from “development” and “productized.” Most organizations have separate teams who are the only ones who are responsible for, or even allowed, to maintain the “production environment.” That just doesn’t seem to make sense in an Agile world, where we are trying to minimize waste, maximize collaboration and turn concepts into cash quickly.
So, let’s reconsider the role of the continuous integration server. Most teams start by using their continuous integration server to be a continuous build Server. We have a uniform script that builds and packages the code, and allows us to make sure that the quality of everyone’s build is uniformly perfect. When we trigger our builds to occur each time that code is checked in to the code repository, we never have to say that we’re sorry that the code we checked in last week redefines or conflicts with methods or namespaces from someone else.
But we recognize that simply satisfying the compiler and packager is a pretty shallow compliment to pay when we’re looking for quality code. So, the team will then start to add unit tests (ideally, they write these unit tests first, and with the code, as part of a test-driven development process). Then, we instruct the continuous integration server to execute all of these short running unit tests after the compilation step is complete on our continuous integration server. Now, we have a lot more confidence about the code. We are sure that our next code and tests play nicely with all of the existing code and tests. Are we done yet? Of course not!
We can start running longer running acceptance level tests, testing complete swaths through the code, and simulate what a “QA” person might otherwise do as part of a regression suite. These longer running tests may take too much time to run after each and every code check-in, so we might schedule those tests to run on the continuous integration server at night, when everyone is home sleeping (with smiles on their faces, knowing that they didn’t leave the code “broken.”) Are we done yet? Of course not!
The next thing we need to do is break down the wall that separates the integration server from the more “stable” QA server. This may take quite a bit of thought to do properly. For example, data in the databases on the QA machine may have to be stable, even while code around them changes (there will be a future article on some tactics on how to do this.) There will be issues with managing application servers, Web servers, and so on, so that they can gracefully be stopped while new packages are deployed on them, and then services restarted. We may want to run quick “smoke tests” to ensure that the deployment went successfully. In fact, we are looking to automate all the things that the person who deploys to the QA server normally does, only now we can make a deployment into QA with the push of a button, the turn of a clock, a successful deployment of a depended upon subsystem, or anything else that we can think of.
What’s so great about getting to this level of sophistication, you ask? Here’s what’s so great – the QA machine is usually made in the production environment’s image. It represents a potentially future version of what production will look like should we hear the words “Ship It!” And, if we did our job right, it should be a simple thing to configure the same steps that we used to deploy to QA, and point to production instead! When we get to this level of sophistication, we are in a similar situation to Sir Galahad on his quest to for the Holy Grail. But instead of preparing ourselves merely spiritually and mentally, we have engineered our continuous integration server to accept God’s grace.
Now that we know the benefits, you might ask how we get there. In Intro to integration: Automation from version control to deployment, I lay out the series of actions your team can take to implement automation and testing all the way from checking the code to deployment into production.
For a comprehensive resource on continuous integration, see Continuous integration: Achieving speed and quality in release management.
Learn what containers have to do with CI and CD