I recently had a discussion with someone at an event about how challenging it can be to transition to an Agile software development process on an existing project. The most common approach seems to be focused on going Agile with the new code and leaving old code in its current state. This avoids the distraction of implementing tons of unit tests for the legacy code, while enabling the benefits of Agile for new code. This sounds simple in theory, but there are a number of details that make it difficult to implement and enforce.
One common strategy involves segmenting the legacy code and the new code so that they can be treated separately. For example, some teams wrap the legacy code in a package or library. They establish Agile goals for the new code, for example making sure developers write unit tests along with the code. In theory, they get high quality, well tested new code while avoiding the burden of backfilling tests for the legacy code.
It’s a good idea, but it makes one crucial assumption: that testing only the new code is the best way to appreciably improve the quality of the product. Testing the new code is definitely a good start but that new code is still interacting with the old code. In fact, new code often uses legacy code in ways that require changes to the legacy code—which can introduce bugs in legacy code paths. Testing only the new code verifies that the new code paths act correctly, but does nothing to verify that problems were not introduced in impacted legacy paths. You can try to test both new and impacted paths, but how can you enforce testing of the impacted paths when you know you don’t have good coverage of legacy code?
And as you broaden the scope to include some of the legacy code, you increase the effort needed to write the tests—one of the things that focusing on the new code was supposed to avoid. Fortunately, not all paths are testable and some paths are more important to test than others. By focusing testing on the important, testable, new and impacted paths you can expend reasonable effort to make meaningful improvements to product quality, while becoming increasingly agile.
Coverity’s testing solutions grew out of our own transition to Agile, and address exactly these problems. Instead of rigidly segmenting testing requirements based on file location or code age, you can require tests for code paths exposed by recent changes. This is done as part of an automated Agile process, automatically alerting developers to specific instances of insufficiently tested code rather than requiring manual review of coverage reports. By understanding the appropriateness of each tested and untested line, your reports clearly convey your success with no risk of misinterpretation.
If you’d like to hear more about how we did this transition, our senior vice president of R&D has done a great series of (short!) videos that explain it in more detail.