30 April 2012
Why refactoring code is almost always better than rewriting it
Developers and architects like to build things, so their initial impulse is often to flatten the place, lay some stronger foundations and build something impressive. It can be difficult to get them excited about incremental innovation, even when this is generally the most sensible approach from both a technical and commercial perspective.
Joel Spolsky wrote a post back in 2000 about Netscape’s decision to rewrite their browser from scratch in the late 1990s. It’s as relevant today as it was more than ten years ago. He described the rewrite as “the single worst strategic mistake that any software company can make” and I am inclined to agree. It took several years during which Netscape watched helplessly as their market share plummeted. Would iterative releases of a troubled code base really have been so bad an alternative?
Developers can’t help it – they just want to write new code
Netscape provides an extreme example but technical teams often lobby to rewrite “legacy” code. The argument in favour of a rewrite is pretty obvious to them. The code is riddled with technical debt accumulated through years of compromises and quick fixes. It’s slowing them down and restricting their ability to deliver enhancements. This may be true to a degree, but it’s often the developer’s natural reluctance to work with code written by somebody else that lies at the heart of this argument.
Peter Hallam suggested that developers spend more time understanding code then they do actually writing it. If you are modifying code then you have to take the time to figure out what it does before you can adjust it. Understanding code written by somebody else requires considerable patience and it can be very tempting to dive in and start writing your own version instead.
Developers tend to be dismissive of code written by somebody else. This is often unfair as any difficulties may caused unfamiliaty rather than the original author’s poor technique. Everybody has their own coding style, their favourite tricks and their own way of doing things. No coding standards will overcome the fact that another developer’s code is always going to seem strange. Better developers will generally have the discipline to understand why code has been written a certain way before dismissing it.
Nobody sets out to write bad code
Any developer who is working on a long-established code base will tell you what a mess it is. There will be horror stories of methods several hundred lines long and classes of byzantine complexity. If only they were given the chance to rewrite it they would produce a clean, focused architecture that would be good for many years to come.
The problem is that nobody ever means to write bad code. Bad code happens to good developers. Requirements change, deadlines requires “quick and dirty” solutions that are never remedied, requirements turn out to be more complex than you originally thought. Software development does not happen in isolation and it has to interact with an uncertain environment dominated by unstable budgets and behaviour.
Technical debt is a fact of life on every software development project and it is not going to be completely cleared by a rewrite. You are just as likely to exchange one set of issues for another. You can try to protect yourself with flexible software design, create loosely coupled components and define clear responsibilities. Whatever you do, another set of developers will still come along in a few years and dismiss your code a muddle.
How do you measure technical debt?
It may seem obvious to developers that it’s time to pay down the technical debt that has built up through years of quick fixes, but it’s generally less clear to commercial stakeholders. Code rewrite projects are difficult to sell because you aren’t necessarily delivering anything new. The stakeholders have already paid to have this stuff written and feel that they are being asked to pay again just to have it written properly.
It is difficult to quantify technical debt in any meaningful way because it is generally justified in terms of its effect on productivity. This is notoriously tricky to measure in any meaningful way due to difficulties in identifying reliable metrics for typical development activities such as implementing new features and fixing bugs.
Given how hard it is to build a meaningful business case for a code rewrite it is often easier to focus on improving smaller, more specific functional areas. If it works out then you will find it easier to justify further investment in improvement. An iterative approach based on refactoring is always easier to sell than a big bang with vaguely defined returns.
There does come a point at which technical debt becomes overwhelming, of course. Old code can be left for so long that it can fall too far behind prevailing industry technologies. This can create compatibility problems and form a genuine barrier to new feature development. Attracting and retaining a development team can become difficult and expensive if your code is based on legacy technology. This is the point at which iterative refactoring can start to seem like an anaemic response.
That messy code is more valuable than it looks
Bear in mind that the large slab of nasty source code that few people really understand often represents many years of investment and effort. It’s a major chunk of intellectual property that underpins revenue generation and you should think carefully before throwing it away.
Established code contains a lot of encapsulated knowledge earned through years of testing and real world use. Old code may be cantankerous and difficult to understand, but it usually works. There is no reason why your replacement system will be more reliable than the original, in fact, it will probably be worse as you find mistakes to make and create a new set of bugs to find and fix.