This is the first post in a new series of posts called Seven Strategies for Burning Down Risk based on the section with the same name in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.
Of all the agile technical practices the first and most important one for reducing risk and giving a true measure of our progress is continuous integration. The very first principle of the Agile Manifesto says “Our highest priority is to satisfy the customer through early and continuous delivery of valuable software.”
We may not want to deliver continuously. When we release is a business decision based on business factors such as the seasonality of our products, support contracts, etc. However, we almost always want to practice continuous deliverability, meaning that we always want to have a buildable system. Only when a feature is integrated into the build shall it be considered done.
Continuous integration is the only way that we can get potentially shippable software at the end of a sprint. Most teams have code that they passed over to QA at the end of a sprint. If that’s the case for your team then I have news for you, you’re doing waterfall, in my opinion. As soon as we start to spread out the phases of development then we are no longer doing agile. In practice, it turns out that integrating the phases of software development is pretty straightforward, with the exception of QA.
Agile quality assurance looks very different than traditional quality assurance. Testing has to happen in tandem with development and in some cases even before development begins. How is that even possible? Well, remember software is soft. We can take advantage of the unique features of software to build into our code self-verifiability. This is essentially what test-first development does and we end up with a suite of unit tests that validate the behaviors of a system.
I find that the value of a continuous integration system depends on the value of the tests. When we write tests that directly validate the behaviors that we want to create then those tests can serve us later when we refactor our code. But if we write tests that have dependencies on implementation details or validate the wrong things then they might break when we refactor our code, becoming a burden rather than supporting us. This is why having the right tests is so vitally important when building software.
It all really starts and ends with the unit test. We want to test the smallest verifiable behaviors that we can identify in our systems. This is really not a testing activity but an automated verification process. We are automating the validation that our features work as expected. We are not trying to assure quality because that’s impossible. One can never assure quality in a complex system, we can only create quality. What we are trying to do with our unit tests is to demonstrate that our code works as intended.
But even more important than using our unit tests to validate behaviors we use them as scaffolding to help us form the behaviors at the right level of abstraction and for those of us who practice writing the test first, we find it an invaluable activity.
When teams are just starting out with agile I recommend that they begin with continuous integration. CI is a foundational practice and creates the infrastructure and context for all the other agile technical practices. All the software needed to set up a CI server is free and you can set it up on an old PC or even on a virtual machine. It runs in the background looking for changes in your version control system and when detected it invokes a build and then runs your tests.
The power and magic of CI come from your suite of unit tests. Like all medicine, your tests can either kill you or cure you depending upon how you use them. Knowing how to write good unit tests for behaviors allows you to map out and conceptualize services so that their testable and straightforward to work with. All these benefits come from integrating code continuously. This is why I see continuous integration as the very foundation of agile software development.
In 2020, I finished a series of 72 blog posts that expanded on the first set of “Seven Strategies…” for each practice in my book Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software. I included two sets of “Seven Strategies…” in my book and so I am expanding on the second set here. The next seven blog posts are expanded versions from “Seven Strategies for Buring Down Risk”. You can find the original post here.
Previous Post: « Keep Stories Testable
Next Post: Avoid Branching »