The Felix code base is now over 10 years old. Conceptually, it’s a bit older than that, but I made the last complete rewrite about 10 years ago.
Since then, I’ve avoided any major rewrites of the code, instead improving and evolving the existing code base. As your code ages, a rewrite can start to seem attractive, but I believe it should be avoided if at all possible.
The allure of the big rewrite
The big rewrite can seem very attractive. Your code base is getting hairier with every new kludge you have to slap on top of it. And with all the experience you’ve gained writing the software, you think you could do so much better if you started over from scratch. Technology has progressed as well, opening new possibilities that are not possible with the current code base.
Or at least, that’s how it seems.
Paved with good intentions
Chad Fowler did a fantastic job of outlining the massive problems with the big rewrite, so I won’t reiterate them here. I’ll just summarize by saying that the road to the rewrite is paved with good intentions, but very often ends in disaster.
It follows that a rewrite is fraught with peril, and should only be undertaken with careful consideration, after all other avenues have been exhausted.
How to avoid the rewrite
The rewrite becomes more attractive as your code base becomes less maintainable. Here’s a completely gratuitous illustration of this:
It’s therefore imperative to keep your code maintainable, and to actually improve its ease of maintenance over time. The single biggest thing that makes it possible to keep a venerable code base maintainable is refactoring. As you work on code, entropy will slowly but surely make it degrade into a big ball of mud. While it’s impossible to stop entropy, you can reverse it locally by introducing order from an external source. This ordering is called refactoring.
There are both technical and human challenges that must be overcome in order to refactor code successfully.
There are two technical requirements for refactoring: unit tests and source code control (SCC).
Refactoring changes the structure of code without changing its functionality; unit tests help make sure that your changes don’t change the functionality.
You also need source code control. If one of your changes breaks something — and you might not find out that it breaks something until weeks or months down the road — you need to be able to go back to your repository, find where the error was introduced, and back out that change if necessary.
In the developer community, there’s a fair amount of resistance against both of these, or at least against their universal use. There always seems to be an excuse why unit tests or SCC can’t be used on this project. But while the efficiency of unit tests in catching out bugs may be debatable, unit tests are extremely valuable when it comes to refactoring. Although the Armadillo (now Software Passport) developers once said that they refactored without a unit-test safety net (they were just “very careful” instead), unit tests are essential for mere mortals like me.
Michael Feathers’ Working Effectively with Legacy Code is a great handbook for making old code bases more maintainable, mostly by getting them into a unit-test harness.
Successful refactoring also requires a clued-in person to be in charge of the code. People who can only see code as a black box will understandably be resistant to spending time “prettying it up,” without any tangible benefits (fixed bugs or new features). It takes someone who understands about technical debt, and who knows what it’s like to deal with well factored code versus bubblegum and baling wire.
Fortunately for me, I’m the one in charge of my code, and I have been since the start. If I decide that the code needs to be refactored, then it gets refactored. I don’t need to plead my case to some project manager who will never have to deal with the actual code, and whose only goal is to meet some artificial milestone.
Beware gold plating
There is a danger in giving the developers control over refactoring: gold plating, or refactoring for refactoring’s sake. This is the grain of truth underlying the pushback of non-technical PM’s. If they can’t tell whether refactoring is necessary, they tend to assume it isn’t.
So refactoring does take some discipline. I maintain discipline by limiting how and when I refactor. Firstly, after a release I allow myself about a week of free reign to refactor. This is when the code base as a whole is freshest in my mind, and a state where there are most likely to have been kludges to get a given fix or feature implemented.
Outside of my “weapons free” refactoring periods, I limit refactoring to the code I’m working on at the moment. If I’m working on the templating system, I don’t worry about other parts of the code base. But I do follow a policy of leaving code in a better state than when I found it. So I will refactor the files that I edit (but I make sure to check in my behavior-changing edits and refactoring edits separately).
The big rewrite is the nuclear option of software development. It’s much better to evolve and maintain an existing, productive code base than to rewrite it from scratch. And the key to keeping a code base maintainable is refactoring. Overcoming the obstacles to refactoring is thus one of the keys to ensuring a successful software product over the long term.
Note that although I did rewrite the Felix code base from scratch twice, those first two versions were more conceptual prototypes than intended to be working programs, so I don’t think they violated the rule (although I have, unfortunately, violated the no-rewrite rule on other projects).
3 replies on “Working with an old code base”
After reading this, I was reminded of Joel Spolsky’s “Things You Should Never Do, Part I,” in which he describes how deciding to do a complete rewrite ultimately doomed Netscape 6 before it was even released.
The unfortunate decision to start again from scratch has had undesirable effects on many great applications, but I’m glad to hear that Felix will be spared such a fate!
On the other hand, the first version of Half Life was salvaged by a complete rewrite.
I suspect the rules are:
1. if you are racing against competitors (like Netscape or WordPerfect were) do not do the rewrite.
2. if you don’t fully understand the original code then do not do the rewrite.
3. if you wrote the original code base while you were a copmlete noob and have learned alot in the process, do the rewrite if the schedule can be slipped.
If you’ve got a buggy mess that was never working, then a rewrite makes more sense. As Brooks said: plan to throw one away — you will anyway.
But when you’ve got working software, it’s almost always far more productive to get it into maintainable shape than to start over from scratch.