Leave No Trace – Testing for Legacy Codebases

We’ve all inherited projects, either when starting new jobs or taking on new clients, that might have been untested, hard to test, or had poor/untrusted test coverage. This creates significant challenges when learning what the code is suppose to do, before you even try to improve, add, or make changes to what’s there.

You have to balance two things in this scenario-a huge backlog(they hired you to do something most likely) and the lack of good testing. Should I either spend my first couple of months writing tests or should I instead start working through the backlog and when I have time, I’ll add tests?

The ugly truth is-you will never “have time”. You will never one day wake up, discover an empty Ice Box, and suddenly start spending your days in the whimsical world of improving code coverage and code quality. With that in mind, how should you begin chipping away towards 100% test coverage?

Uncle Bob proposed a few years ago the  “Boy Scout Rule” . He suggested that with every commit you should find something to fix in the codebase to make it better.  I like to go a little closer to the original Leave No Trace principle (at least how my Boy Scout troop implemented it). Leave No Trace applies to the campsite YOU stayed at, and the trails YOU hiked. Not the entire wilderness. So make the code better that you used or referenced, not some distant piece of code that’s easy to fix.

Enter “Leave No Trace Testing”, very simply means, “Leave it with Tests”.  So to apply Leave No Trace Testing, whenever you touch a part of the code, you add testing to it. This applies definitely to any new code you’re writing, and it should also apply to any methods or functions you’re using as apart of your code.  Use a transformation on a model? Add a test for what you expect the input/output should be. This will ensure that someone doesn’t go in and change how the model is presenting itself to your part of the code.  It will also make it easier later on for you to understand what it does, and potentially refactor it if need be. I’m not suggesting you add tests to every disparate part of the codebase that you touch all the way down the stack, that would take forever. Instead be reasonable, responsible, and do enough that you feel like you’ve contributed to a better codebase. If everyone does a few tests outside their own new code everyday, your test coverage will start to creep up, and over a few short months you’ll have a happier, healthier codebase.

But Scott! My problem isn’t no tests, it’s mainly the negative test cases that I’m missing for existing coverage. 

Great! Add them when you’re in that part of the code. You don’t have to add all of them at once, but start chipping away.  Use a tool that runs your tests and shows you what parts of your code get called, and which don’t. This will show you all the cases where the negative cases don’t ever get run. Tools like rcov (Ruby), xdebug(PHP), or any of the thousand Java coverage suites.

I’ve found this is a great way to work towards 100% test coverage that’s low impact enough the whole team can buy into it. You can’t rely solely on the coverage scenarios by themselves (you can trick them into thinking you have all the coverage you need), but they’re a good start and a fun metric to compete against.

If you need help understanding WHAT to test, Sandi Metz does a great talk on “The Magic Tricks of Testing”.

And a related talk that I’ll expound more on in another post, checkout Katrina Owen’s “Therapeutic Refactoring” which talks a lot about the challenges of dealing with bad code and her compulsion to improve it.

Are you going to try Leave No Trace Testing in your code? Disagree with this approach? Tweet me @scottefein

Leave a Reply

Your email address will not be published. Required fields are marked *