One project I have been working on recently is a code upgrade of an existing enterprise web application. It is not a re-engineering effort, but instead only consists of necessary code changes to get the application working with the latest versions of the system software. The application had been in use for several years, and the existing development effort was concentrated on new features and maintenance, and the customer only prioritized the shift as they recognized many infrastructure issues.
The project was an illustration of how good code written in the past can become terrible even if no one has touched it. As Lewis Carroll wrote through the mouth of the Red Queen, “it takes all the running you can do, to keep in the same place“. Here are some thoughts:
- If you have the ability to choose a particular programming language and platform (your expertise being equal in the choices available), you would most likely make a different decision at different times. A choice made with good reasons years ago may seem bad today.
- How some of the relative strengths of different languages change over time and others don’t. When it was a released, C# was a joke, an impostor to Java. Today, with Java’s stagnation, it is light years ahead. But when it comes to third party software (especially open source), Java still has considerable strength.
- The progress of languages and availability of external packages and utilities makes new code look entirely different from older code, even though they are both written in the same language.
- The increase in test-driven development (along with the network-driven popularity of some frameworks) has also driven changes in the organization of code, something that had to be previously driven by disciplined adherence to standards. In other words, falling into the pit of success.
- How many millions of man-years have been wasted on writing code for complex frameworks designed by bureaucrats? Not to mention money spent on earning silly certifications to prove yourself competent in them. (You know what I am referring to!)
- Convention over configuration. A simple concept that makes for incredible productivity.
- In the short-term (as measured in months), it is probably less relevant. But when an application use spans years, the gains from better hardware makes a mockery of many code-level decisions for performance optimization. In the long run, it is always better to write better, decoupled code than have to write code that has to work around hacks.
The great moments in refactoring existing code are when you can simply delete vast chunks because they are superfluous or because you can drop in some external library to do the same work. But most changes require a scalpel instead of an ax. The typical approach is to build “scaffolding” by writing test cases that work against the existing code and then change the code as necessary while continuing to pass the test cases. This ensures that the upgraded product not only contains good code, but has been verified to exhibit the same behavior.