What comes to mind when you hear legacy code? Many developers cringe at the mention of legacy code. They imagine massive code files, old technologies, and code that hasn’t been touched in years, somehow becoming worse because time has passed. Lucky for us, code doesn’t really “age”. Technologies come and go but coding principles often stand the test of time. Michael Feathers presents a unique definition of legacy code that not many developers would think of: legacy code is simply code without tests.
Over the past 8 weeks, we have had the opportunity to meet with 15+ developers to explore and discuss the ideas presented in Working Effectively With Legacy Code by Michael Feathers.
Wait, so you’re saying the system built 10 years ago that has a full suite of high quality tests is NOT legacy code? Correct. When developers return to a codebase with tests after a long period of time they avoid the dreaded FDD – Fear Driven Development. They can make changes with confidence and make them more quickly, gaining near-instant feedback from the tests.
While walking into a project with a quality suite of tests is great, that is not the main point of Working Effectively With Legacy Code. Michael Feathers took us through numerous scenarios we may come across in legacy code. He taught us how to get that code under test by showing techniques of parameterizing methods, extracting interfaces, or adding interception points. The book gave us a full bag of tricks to employ when we find ourselves in a situation where building tests is difficult. By applying these methods we can move towards more tests, applying SOLID principles, and reducing side effects.
Feathers describes the two main methods for modifying legacy code as Edit and Pray and Cover and Modify. With an Edit and Pray technique we make changes, do manual testing and hope that we didn’t break the system elsewhere. This is dangerous and can lead to unintended bugs sneaking through our manual testing. It can also lead to a testing ice cream cone anti-pattern appearing. The preferred Cover and Modify technique has us building tests over the code we need to change and then making our modifications to ensure our changes are safe. By putting a testing “vise” on our code, we are able to tell exactly which pieces of functionality have changed and which pieces haven’t.
This book was published over 15 years ago, but the principles and methods are just as applicable today as they were then. With the advent of DevOps happening after the book was published, it does not touch on the value of tests in DevOps practices. Building automated tests into legacy code allows us to run the tests in a pipeline and validate pull requests to protect our master branches and make releases safer.
The book’s main theme is that building tests and improving architecture for legacy code is a continual process. There may be intermediate steps where the code doesn’t look as great, but the short term goal is to make it testable. Once the code is under test we can take further steps to clean up the code and use those tests to give us confidence as we go. We plan to bring these practices into future legacy code scenarios, apply them to the new code we write (to avoid creating legacy code) and use them to create better outcomes for the teams we work with.