Last saturday, I had the chance to facilitate a code retreat with Haikel Guémar and Charles Bouttaz. The subject was legacy code, and we tried to explain it with an unusual approach. Instead of the classic coding katas like gilded rose or ugly trivia, we decided to explore the very definition of legacy code, and the conditions under which it is created.
After all, legacy code is probably one of the things with the worst name in our industry. Chad Fowler gave an excellent talk on the subject.
I believe we call it “legacy code”, but we think, “awful code that I am afraid to change”. The literal definition of legacy code should be “code that I inherit from someone else”, but we don’t really speak of legacy code in that way.
A common definition is “code which runs in production”. But one of my favorite definition used to be from Michael Feathers: “Legacy code is code without tests”.
Thus, during this code retreat, we started by creating their own legacy code, following the Feathers definition. In the first iteration, we use the DNA kata, which is very close of fruit shop kata (but you manipulate string instead of integers). We added constraints like no unit testing, no pairing, and an incentive to create haste: a book for the first to complete the exercise (the book was, of course, Working Effectively With Legacy Code).
The following iterations started with the code produced in this first one, and we added other constraints like removing implicit dependencies, pair programming, unit testing etc. We observe different things during the retrospectives:
- When working on your own code, you did not always feel the necessity to add tests, especially if you’re using a type safe functional language
- When working in an unknown environment, or with unknown code (even if you knew what it should do), you feel the necessity to add tests
- Most of the time, even if we proposed new functionality to add after the first iteration, the whole iteration was spent paying down technical debt from the first iteration (refactoring with or without tests)
I think we can draw different conclusions from that.
The first is that it is easy to create an environment to write bad code: put pressure on people, short sighted vision, micro-management and working in isolation.
The second, and it was a word we heard a lot during the retrospectives, is that to produce good code, you need to be safe. You need to be in a safe environment, with safe code that you don’t fear breaking with each refactoring.
And I think this is a better definition :
Legacy Code is code you are afraid to change.
It could be because it is in production, and/or because it has no tests, the point is: you fear changing it. It will lead you to write more bad code with lots of copy/paste and other common anti-patterns.
So, if you do not want to work on legacy code, start by producing code that you and your colleagues won’t be afraid to change. Yes, this is almost impossible without tests, so don’t take the risk, and write tested code.
Special thanks to Brian Gibson for the kind review