Good technical practices (such as those typical in DevOps environments) can help the government in contracting for information technology services. We should require these technical practices in our IT services contracts, and if we are investing in QA and independent verification, we should invest first on validating good technical practices. Let me give a few examples. For readers without a technical background, you should be able to find more information about these practices online.
Good, state-of-the-art testing practices are important for more than the obvious reasons. Most tests should be automated and should follow the classic “testing pyramid” (many unit tests, somewhat fewer integration tests, and fewer tests at the user interface level). The automated tests themselves are just as important a deliverable from the contractor as the code itself.
There are many reasons why such automated tests are important in our contracting environment. The automated tests serve as regression tests that will speed later work on the system. If a second contractor does something that “breaks” the first contractor’s code, it will immediately be spotted; in essence, the tests can be said to “protect” the first contractor’s code. If a new contractor is brought in for O&M or future development, the automated tests serve as documentation of the requirements and allow the new contractor to be confident in making changes or refactoring – they are OK as long as the regression tests continue to pass.
Scripted deployments and “infrastructure as code” serve a similar function. By providing automated scripts to set up the production environment and deploy code, the contractor is documenting the deployment process (and reducing the amount and cost of paper documentation!). No longer is the knowledge just in their heads (making it costly to replace the contractor). Deployment scripts can be tested, making them an even more valuable form of documentation. They can be placed under version control and audited, increasing security.
Continuous integration increases our ability to work with multiple contractors and gives us more confidence in a contractor’s status reports. By continuously integrating code we ensure that code from multiple contractors will interoperate, and we avoid last-minute surprises when a contractor’s 100% finished work fails to integrate.
A zero-defect mentality where user stories are tested immediately and defects are remediated immediately ensures that code the contractor says is finished really is finished. It avoids passing defective code from one contractor to another; reduces finger-pointing; and makes integrating code simpler. If we are comparing contractor performance it serves as an equalizer – if one contractor finishes 10 stories and leaves 15 defects while another contractor finishes 8 similarly sized stories and leaves only 12 defects, which has performed better? We can’t know. Zero known defects should be our expectation.
The last practice I will mention is the use of good design patterns and architectures that feature loose coupling. Good use of design patterns makes it easier for a new contractor to understand the code they inherit. By encapsulating pieces of the system it can make it easier to have multiple contractors work in parallel and even at different paces.
Together, these practices can make it easier to judge contractor performance, allow us to partition work between a number of contractors, and make it easy to switch contractors over time.
(thanks to Robert Read at 18F for some of these ideas)