A software project can become “legacy” after just three months from its inception. I’ve recently seen many projects that look OK on the surface, but are in fact so “broken”, that they have to be rewritten. Well, they work, but continuing their support is a pain. And everyone has probably seen at least one such project, where you want to just throw it away and start from scratch. The problem is — starting from scratch won’t guarantee a good outcome either. Why does this happen?
I think it’s because developers tend to solve problems one at a time, achieving a “local maximum”. They don’t need to be “quick dirty fixes” — any even reasonable-sounding fix for the particular problem at hand may yield de-facto legacy code. And if we view software development as constant problem fixing (even introducing new features consists of fixing problems along the way), we have a problem.
My approach to that process is to take a step back and ask the question “is this really the problem I’m solving, or is there a bigger underlying problem”. And sometimes there is. There are some clear indications when there is such a problem. “Code smell” is a popular term for that, but I’d like to extend it — sometimes it’s not the thing that you do that makes things smell, but rather something done before makes you make bad decisions later. Sometimes these decisions may not even look wrong in the context that you’ve created with your previous decisions, but they are certainly wrong. And you can use them as indicators.
If you have to copy-paste some piece of code to another part of the project, and that’s your best option, something’s wrong with the code. You should take a step back and refactor, rather than copy-pasting yet another piece.
If you have to rely on a full manual test to figure out that your application is not broken, the quick and easy “fix” for the problem is to just get a QA to manually test it. If you do that, instead of adding tests, quality degrades over time.
If you have to use business logic to overcome data model or infrastructure deficiencies, the easy fix is to just add a couple of if’s here and there. Then six months later you have unreadable code, full of bits irrelevant to the actual business logic. Instead, fix the data model or your infrastructure (in a wider sense, e.g. framework configuration).
If, given a bug report, tracing the program flow given requires knowing where things are, rather than finding them, it means the project is not well structured. Yes, you are probably working on it for a year now and you know where things are, but finding components using search (or call and class hierarchies) is the proper way to go — even for people experienced with the project (not to mention newcomers).
If the addition of a data field or a component requires changes in the whole project, rather than just an isolated part of the project, then each new addition creates more complexity and more potential failures. E.g. pseudo-plugin systems that require changing the core with each plugin.
The list can go on, but the point is clear – if faced with an option to do something quickly but the wrong way, then take a step back and rethink whether the problem should exist in the first place. Otherwise each fix becomes technical debt. And in three months you have a “legacy” project.