Both times sucked, both times we underestimated the amount of work to be done, and both times we were much happier with the new product than what we had before.
Both times sucked, both times we underestimated the amount of work to be done, and both times we were much happier with the new product than what we had before.
The Relentless Improvement model that many refactoring advocates espouse is one (great) answer to that question. But it also means they won't wait for a rewrite. They'll fix the bullshit they're staring at today, and stare at some other bullshit tomorrow.
The trick is in making those docs so small and lean that they fit neatly in comments inlined with key functions, and this I have accomplished by reduction to a list of "must" and "cannot" (abbreviated to + and -) bullet point items. It must function like this, it cannot function like that. Some requirements can be turned into automated tests or static checks, others need an eye to verify. But the cycle I'm seeing often works out to be: write speculative requirements, write speculative code, then reverse-engineer true requirements and maintain code against that.
Doing this creates a base quality bar that the code has to hit in both the rewrite and refactor cases. The trap that rewrites tend to fall into is that the requirements are lost during the rewrite, so the new code becomes just as speculative as the original greenfield code. If you have the requirements as your reference, on the other hand, the area of bad code you can write is limited to the boundaries of those requirements and any missing requirements. This makes new code much less of a gamble.
It's so hard to get people to write good requirements that we often overlook (or rather, won't look at it) as a solution.
Rewrites are easier with good requirements. They're a constant stream of bad surprises without them.
It's possible, while refactoring, to begin to collect those requirements, in the tests if nowhere else. But it's not a given.