This reminds me of the Joel Spolsky's post on why you should never re-write your code from scratch [1].
The reasoning goes most 'ugliness' comes from bug-fixes that people encountered along the way, and by re-writing that 'two page function' you lose all that accumulated knowledge. In short, the hacks that make us want to rewrite code are there for a reason
That article by Joel Spolsky was one of the few I can't fully agree with. The greatest successes of my career have often been in creating "next generation" implementations of software using newer or better technology and designs. Those re-implementations have resulted in very significant performance and productivity gains. While rewriting things from scratch can definitely be a naive impulse, there are times when it's the right way to improve a product, and if you fail to do so, your competitors will. The trend towards microservices tacitly recognizes this, I think; one of the tenets being it should take a small team a couple of weeks to rewrite any component of the system from scratch. When software was written poorly to begin with and has grown unmanageable by layering hack upon hack, it can pay to take a step back, look at the actual requirements, and consider if there's a better way.
I wholeheartedly agree with what you said. There is a time and a place for leaving legacy code alone. I also know from first hand experience some things are just too far down the hacky rabbit hole and you just gotta cut of the dead stuff and start over sometimes. But on the other hand, the only times I've done that in my career the code was written in a very anti-pattern style pretty much against all language guidelines and had hidden side effects everywhere. It was a mess to figure out how some features worked so I just rewrote some features in 1/10th of the time it took the original maintainer to piece in similar bug fixes in the legacy code.
In my experience, it's extremely difficult to know which particular lines of code are obsolete. This becomes even more difficult as a codebase ages and is worked on by more contributors. Add on another exponent for every business stakeholder that has a hand in defining the business rules—especially if you have requirements coming from multiple sources that may not be aware of each other.
This is why we have the strangler pattern, right? You wrap the legacy functionality with a set of tests that define the functional requirements of the code, then your new refactored solution needs to keep those tests passing. (And then once you've done that, make sure that your "hacks and bugfixes" get test cases so you can make sure the next refactor accounts for them!)
Which is why comprehensive testing is important. If we have test cases that accurately capture our requirements, then we can refactor and know when we've err'd from the path. If we don't have test cases, then we have to make best guesses (and we know how that one turns out).
Accuracy is key, here. I came into my current job with some pretty awful tests in place that “passed” and showed good coverage but did absolutely nothing for actually hitting the necessary cases...
I worked with one guy once where he wanted the "ultimate flexibility" and every data model object basically became a database table with the columns 'key' and 'value'. Trying to think of everything ahead of time has its own problems...
Actually, what that piece underpins is not that you shouldn't do rewrites, but that you shouldn't do that with the expense of the developer resources of your legacy application, you shouldn't put your old product "on hold" for the period of the rewrite, nor should you pretend that you are modifying the old product, but that rewrite effectively always results in a new product, and you shouldn't except that you can hotswap the new product with the old one.
The age old maxim "sell old products to old customers, and new products to new customers" works pretty well here as well.
How to rewrite the product if all your experts are tied up in the old product is a different question entirely.
For example of industry that seems to be in perpetual rewrite is the gaming industry. But they don't rewrite the old games, the develop new products using partly the existing concepts and software modules.
This product based view also tells you something: when in business, you should never do a rewrite out of purely technical aesthetic reasons, but because of concrete measurable business goals.
Rewriting the code from scratch does not necessarily mean clean-room reimplementation! It doesn't even mean you can't include parts of the original in the new code.
Rewriting from scratch means rethinking the design, or basic structure, of the code while keeping all its functionality. It's not about getting rid of hacks, it's about lining them up neatly in one place instead of having them all mixed, twisted, nested and threaded through the code everywhere.
Well, at least if you know what you're doing, instead of just randomly deciding on a rewrite because it's more interesting that way or because you can't be bothered to read and understand the original. Then, I agree completely with Joel, it's most of the time a grave mistake which made countless companies and developers fail.
Isn't this exactly the problem? I know "what I'm doing", but don't have the added knowledge of several engineeers through several years, and rewrite it's not necessarily a 1:1 transcription to a new language or structure.
If something is bothering the team daily, than yes, a rewrite might be needed, which is different, and hardly something I would exclusively by myself.
Exactly this. It’s usually much better to refactor/rewrite small pieces at a time where you can rather than taking a scorched earth approach on the entire codebase, and usually that involves including some of the “hacks” until you can get a better grasp on what the right design is.
My take on this is that it won’t help anyway. If there are problems in the code that have nothing to do with Chesterton, the fact is that the team wrote this code and asking for a do-over is magical thinking.
If you want to fix it, fix it. Find the other people who are fixing it and collaborate.
Advice I should follow myself: If they fight you on that, get out. They like their ball of mud, and they will turn new code into mud too. If you ever succeed, it will take you until it’s time to find a new job just to get things to tolerable. That’s an incredibly foolish investment in being right.
There are more people in the world that can be taught than there are teachers. Don’t waste your efforts on bad pupils. It just reduces our collective intelligence.
I think using "never" should be considered an exaggeration or embellishment, or else clearly disclaim that rewriting code given the existing needs of the business (including the existing code base) is different from writing brand-new code "from scratch".
Here's why: I saw a comment in some thread about machine learning yesterday that I think is a good analogy in some cases for what you describe. The comment essentially said that a "never re-write" policy leads to "overfitting" the code to the problems of the past and therefore less flexibility in implementing both fixes for issues and new feature development.
In theory. In practice, the code will often have corner case behaviours that eludes any of the tests, yet are still important.
As an aside, as I understand it, refactoring was popularised as a response basically to this conundrum - it's a technique for reorganising code without changing its functionality. So you can gradually "rewrite" your two page function without losing the accumulated knowledge.
Unless we really stretch the word "refactor", my "in practice" is different than yours. I've done plenty of re-writes that fixed more unknown bugs than re-opened previously solved ones.
I agree with other responses to this comment. Ugliness might have had a reason, ok. It's important to understand that. The question is whether it has a reason to stay. And here we find the usual balance between the effort it takes to maintain ugly code and the effort it takes writing new one. On another point... I understand calling that "accumulated knowledge". But that's knowledge accumulated very badly. You want to accumulate knowledge? Write tests when needed, comment them when needed, add comments when needed in the comment. That's accumulated knowedge too, and you are actually making it way easier to transfer, which is definitely one of the most important parts. If you were already doing things right, that's another story, but in general, if you look at code and your brain goes "what the..? no, please, why did you... T_T", there's probably a need for a rewrite.
EDIT: I think it's also important to mention the value in rewrites, specially on your own code. Spend a lot of time thinking how to make your code simpler, easier to follow, cleaner. That will help you write much better code in the future. A lot of people would benefit from this if they tried. Writing code that works is simple, writing good code is very hard. And rewriting code teaches a lot of the second. When you are learning, rewrite a lot. When you think you know what you are doing but someone suggests to rewrite (and you know they are good programmers too), at least listen.
> most 'ugliness' comes from bug-fixes that people encountered along the way
That's reality, sure, but there's no reason not to document it properly. Inadequate documentation seems to be assumed implicitly here.
If a (competent) reader is thinking Why on Earth does this code do that?, it means you've written unmaintainable code with too few comments around the necessary-but-ugly workarounds that you've used.
If you have properly commented everything, the reader won't mistake the necessary complexity of the code for a giant-ball-of-mud.
People have been measuring the cost of refactoring versus rewrite from scratch, but the results are, as so often in the industry of software development mostly ignored. Anyway, there (obviously) is a tilting point.
if more than 25% of a component is to be revised, it's more efficient and effective to rewrite it from scratch.
(It's fact 19 in 55 Facts and Fallacies of Software Engineering) which references several sources of it.
Mostly you shouldn't rewrite your code from scratch :-) - some times you need to like when we in the UK had to rewrite a billing system - which was a POS we had inherited from MCI.
The reasoning goes most 'ugliness' comes from bug-fixes that people encountered along the way, and by re-writing that 'two page function' you lose all that accumulated knowledge. In short, the hacks that make us want to rewrite code are there for a reason
[1] https://www.joelonsoftware.com/2000/04/06/things-you-should-...