Good catch. This is precisely the article I had in mind. Yes, I unashamedly think that rewriting from scratch is the wrong kind of laziness.

Take a look at File::Find sometime. What a mess. My refactored version passes all of the tests (at least on a Unixy system) and is *half* the code of the original. How long would have it taken to rewrite that from scratch? Far longer than it took to make incremental changes and get it into a better working order. (If anyone's interested and can debug on a different platform, let me know.)

Mozilla's a great example. Also consider Perl 6. It's been a year and a couple of months, and with all of the brilliant ideas and hard work and smart people, we've got a handful of design documents, a virtual machine that does some math and can print things out, and (admittedly) saner bytecode. This, while we're still fixing bugs and trying to finish the test suite for Perl 5! Do the internals need to be improved? Yeah. Will Perl 6 deliver? Undoubtedly. Why throw away 35 megabytes of source code if Perl 6 will act 95% the same as Perl 5?

Realistically speaking, if you don't have design documents or tests or even a good idea of what the software is doing, how confident can you be that your rewrite will do what it's supposed to do, at the same level? (If it's not working period, that's a different story.)

And, yeah, your process sure smells a lot like "improving the design of existing code." I don't know of any Refactoring gurus who'd claim that you should have x% of the original code left when you're done refactoring.

My point (and maybe Joel would agree) is that though maintenance isn't the fun part of programming, you very rarely have (or should take) the luxury of skipping it.

Ovid, you'll have to write tests sometime. My recommendation is to do it now, based on the old code. You'll get a handle for what it's really supposed to be doing, you'll immediately see how to fix it, and you'll grow as a programmer very quickly.

First take the Netscape/Mozilla project. The article
addressed that one and said that the decision to rewrite
was an unmitigated disaster. And implies strongly that if
they had decided to work with the existing code-base, they
would have had better results. Well the way that I remember
it, they were getting eaten alive by IE, and development
was crippled by having to deal with and work around layers
of bug fixes on bug fixes. The fact that one set of
nightmares came true doesn't mean that the other would not
have. Hindsight isn't 20/20. Rather it is speculation with
the comfort of knowing you will never find out if you are
wrong.

Take next Perl 6. Perl 6 had many major goals. The most
important was to reinvigorate the Perl community. Others
included making it easier to get into the internals and
easier to port to different platforms (eg the JVM,
C#, or a more aggressively optimized binary). Note that
Perl 5 was not doing too well at these tasks despite much
energy and interest. Well Perl 6 has done quite well at
the first, and I am fairly confident that it will be able
to succeed in the others. It just won't do it on an
aggressive schedule.

But I said I don't like to argue from failure. How about
a success? Take a look at perl. Scan for the words
"version 5". Perl 5 is a complete rewrite of Perl 4.
According to Joel that was a horrible mistake, and Perl 5
was bound to fail. Didn't fail that I see. In fact when
I take a look at what it resulted in, I don't think that
features like lexical scope, removing 2/3 of the reserved
keywords, adding references, etc, etc, etc would have
happened in the same timeframe doing incremental
refactoring. Furthermore I give Larry Wall due credit, he
has probably been writing influential free software projects
longer than both of us have been writing software combined.
Given his trail of successes, when he thinks a rewrite is
doable, it probably is. If he thinks that it is a good
idea to get where he wants Perl to get, well he is the one
whose vision got it where it was.

Now this is not to say that refactoring is a bad idea. When
it works, it works well. It is a useful tool. I am glad
that it helped you on File::Find. But I think that
most big projects can profitably use multiple modes. For
instance the Linux project does both. Most of the time you
do incremental ongoing development. But I think that ESR
made the right choice when he made CML 2 a complete rewrite.
Sometimes you incrementally adapt a component. Sometimes
you replace it. You are doing something wrong if you need
to replace big components very often.

And one final thing. Ovid is dealing with a system, one
of whose problems being that it had a bunch of features
added without much rhyme or reason because an ex-employee
thought they were "cool". It does not have a large user
base. I don't think, therefore, that he should build tests
based on the current behaviour, enshrining the misfeatures
in tests. Rather he should do some research about how the
system is actually used, and only test for what people use
from it. Whether or not he rewrites from scratch, blindly
refactoring based on the current behaviour will not solve
one of the problems that he wants to solve. And, whether
or not he rewrites, he should think about how to solve the
business problem. Perl 4 did not stop development just
because Larry was working on Perl 5. Perl 5 is not
stopping active development just because Perl 6 is being
worked on. Creating great software is one thing. But
you need to survive to actually do it...

You're right, we're all speculating with the benefit of hindsight. Probably no one at Mozilla thought they'd spend three just to get something that could honestly replace Netscape 4. (This has suddenly become a Software Quality Rant.)

I would like to see programmers give up the idea that the only way to understand code is to rewrite it. It's nice that new programmers have the energy and inclination to start things from scratch, but we all know where the elephantsgo to die.

I agree that it is good for people to learn to use and maintain wheels instead of building them. I also agree that the experience of maintaining code teaches you a lot about how (not) to write maintainable code. However there are at least three good reasons I would cite for when it is important to rewrite code:

You need something available in the language you are working in, and the original code is not. The example I cited above was rewriting code in Perl that had been in Expect scripts. Without the rewrite you cannot add consistent error handling nearly as easily.

The component has been identified as exceptionally buggy. This is based on research at IBM where they found that they could get impressive improvements in code quality by tracking bug count, and then rewriting from scratch the worst 10% of components. The point being that the components usually had some fundamental design flaw, and your demonstrated odds of writing a better component were very good.

You have completely reconceptualized how the component should work, believe that the benefits will be very large, and accept the costs of an attempt. This is the most dangerous reason, it is easy to fool yourself. However sometimes dramatic change in how something fundamentally works is easiest achieved through a rewrite. (Perl 5 as opposed to Perl 4.) Even so, I think this is an absolute no go unless you make it a priority to support the old model from within the new. The new model needs to not only be better than the old, it needs to subsume it.

But note that while I think there are cases where rewrites are justified, I freely and absolutely agree that people tend to launch into rewrites without sufficient justification...

When putting a smiley right before a closing parenthesis, do you:

Use two parentheses: (Like this: :) )
Use one parenthesis: (Like this: :)
Reverse direction of the smiley: (Like this: (: )
Use angle/square brackets instead of parentheses
Use C-style commenting to set the smiley off from the closing parenthesis
Make the smiley a dunce: (:>
I disapprove of emoticons
Other