There are two sides to the technology curve: 1. incremental change and 2. revolutionary new ideas. I honestly believe the former trumps the latter in most cases. We usually see advancement over a period of years. Agile software development, while faster in many ways, actually encourages the interative and incremental creation of software.
Given that people usually GROW their software, there is inevitably a ton of code in any particular application that the average programmer might review and conclude: “This all sucks. We should write it over from scratch.” Business and project management realities usually enforce a cooler-headed approach. So revolution is rare, but it still happens.
To me, the best software designs allow for both kinds of creation to continue to happen: slow, steady growth and spontaneous component replacement. The key to achieving both is abstraction and intelligent architecture. If something is hardcoded or tightly coupled to something else, there’s a very strong chance it is poorly designed. Abstraction buys you the ability to swap components in and out. But good designs also offer enough structure that future coders on the project can do the incremental changes without painting themselves into a corner. Granted, many coders bring the problems on themselves (like copying and pasting code that should be called by reference). Code forks are so common and they are frequently unnecessary. If a programmer is able to understand and likes the structure of the code they are incrementally changing, there is a much better chance they won’t wrap spaghetti around it to make it work in the new context.
In the book, Code Craft, by Pete Goodliffe, there is a comprehensive list of the symptoms of what he calls “code rot”. Here is a sample:
- There is no structure: it is not clear where to look for a certain bit of function.
- There is high coupling: Complex module interconnections and dependencies mean that a small change in one place ripples out across the entire code, even into seemingly unrelated modules.
- The code is littered with work-arounds: fixes for symptoms but not for causes. They hide the real problems. The edges of the system are cluttered with these, leaving faults lurking at the core.
Goodliffe blames complexity. I blame natural evolution and multiple owners. In either case, the way to prevent it from happening is to build in safeguards up front: code as if you are leaving a legacy, one to be proud of. The term “legacy code” has such negative connotations that I hesitate to use it here, but I really do want everyone to think about how they leave something behind that may well outlive them. (Unhealthy programmer lifestyles and longer system deployments make this flippant statement more and more full of truth with each passing year.)