Mind Matters Natural and Artificial Intelligence News and Analysis
The code is on a laptop on a wooden table in front of the window  in the dark with a view of the lights of the night city, color lighting in the room, home decor
Licensed via Adobe Stock

Know When to Hold ’em, Know When to Fold ’em

Taking control of the rewrite reflex

Computer programmers are a pretty predictable bunch. Every time they approach legacy code, the gut reaction is “let’s rewrite this from scratch.” The reaction is understandable for many reasons. 

First of all, code written by someone else (or even yourself a long time ago) is hard to understand. Even good documentation can’t cover every detail you need to know, and there is nothing that helps you understand the problem better than writing the code yourself.

Second, as time goes on, and you think about a problem, you always come up with better (or at least different) approaches. You might realize that some aspect of your code could be factored out. You might think that rearranging the code would make it cleaner and clearer. Perhaps the API interface has some redundancies or is clunkier than you would like.

Finally, perhaps there is some new system or framework that this can be placed under which will make some of the code unnecessary (and therefore less code to maintain) and add in a bunch of features for free. Perhaps there is a new style of coding altogether that you want to try out, so why not rewrite the present system in the new style?

All of these are perfectly understandable reactions, and, in the right context, they may even be correct. However, I would argue that the “rewrite reflex” is something that both developers and their managers need to learn to tame if they want building software to be an effective strategy for their organization and not just a way to spend corporate money on personal fulfillment.

This sometimes comes from the users as well. Many users want flashier interfaces, written according to the latest and greatest user experience (UX) dictates. Fancy JavaScript controls are requested and highly intricate interactions are desired.

While all of these things may be good in the abstract, the problem is that replacing existing technology has a lot more costs than meet the eye. Rewriting software incurs heavy costs for users and developers that are beyond the initial outlay (even though that might be significant). 

As an example, anytime new software is written (including when it is rewritten), subtle bugs are introduced. The maintenance cycle following a large release of new code is usually much heavier than previous maintenance cycles. In my own case, I’ve had code which, after a year or so of maintenance cycles, required no maintenance whatsoever for seven years. If new code had been written to replace it, then it would likely require another six months or a year of maintenance after the initial release. By the time that year was over, I would probably have yet another idea about how it could be rewritten better! Instead, by leaving it alone, there were seven years where the code automated work without requiring maintenance. Was the interface outdated? Yes. Was the code written in a way that I would be embarrassed to tell you about? For sure. But nonetheless, it did its job, and letting the legacy code stay in place saved the company large amounts of money.

Another place where costs come in is in training. You might think that your new user interface is easy to use. However, the users have gotten used to the way the old system works. This is true even if the users actively complain about the old system. While there are times when the system gets in the way of users doing their job, I have found that about 60-80% of the time, even when users complain about how clunky an interface is, they are actually more productive on the old interface than on any new interface that replaces it. 

Usually, UX designers want to have lots of special-purpose tools for each user task. The problem is that there winds up being an explosion of special-purpose tools, such that the user winds up not being able to keep track of them. Additionally, each of those tools needs to be implemented, managed, bugfixed, and kept in alignment with the rest of the system. This adds development cost (above and beyond what it originally cost) as well as ongoing maintenance costs.

Legacy systems are usually written in a way that matches the underlying workflow of the system. It turns out that fighting the underlying architecture winds up costing a lot of time, money, and effort, and give you little benefit in return—usually confusing the user because they are now unable to see and understand how their data is really structured and used.

For rewrites that concern code issues, going to new platforms is costly not only for the time to rebuild the system, but also for the subtle bugs that are introduced as the developer learns the new platform. Learning all the caveats of new technology takes time, and none of them are immediately apparent. Therefore, moving to a new system will invariably lead to subtle bugs that the developer couldn’t predict ahead of time, and this adds to both the development and maintenance costs.

Additionally, new paradigms are actually more likely to be dispensed with than to gain traction. Perhaps the new framework will take off and be well-supported. However, it may just as likely be abandoned. Perhaps the next version, instead of being a minor release, will change everything, which means that, to keep up with the security fixes, you will need to rewrite your code again. New paradigms can quickly fade. People thought that HATEOAS was going to be the next revolution, but all it wound up doing was causing developers a lot of problems. SOAP kept on promising to be the next big thing, but it wound up being a mostly-Microsoft backwater.

Finally, there is the cost of interacting systems. We often forget how many systems depend on other systems. Rewriting often means having to make modifications in numerous other systems that interact. Sometimes, you only recognize that a system interacts when the interaction starts failing, so the cost is hidden from the initial development.

Here’s the takeaway: Working production code is one of your most valuable assets as a development organization. Rewrites are occasionally necessary, but they should always be done with the utmost trepidation. Care should be taken to make sure that any rewrite actually serves real business needs and not just the personal tastes of the developers or users. The fact that there will be hidden costs and increased initial maintenance costs should be given careful consideration. Finally, you should ask yourself honestly, “Am I going to be wanting to do the same rewrite a year or two from now?” If the answer is yes, then you are probably rewriting for the wrong reason.

Jonathan Bartlett

Senior Fellow, Walter Bradley Center for Natural & Artificial Intelligence
Jonathan Bartlett is a senior software R&D engineer at Specialized Bicycle Components, where he focuses on solving problems that span multiple software teams. Previously he was a senior developer at ITX, where he developed applications for companies across the US. He also offers his time as the Director of The Blyth Institute, focusing on the interplay between mathematics, philosophy, engineering, and science. Jonathan is the author of several textbooks and edited volumes which have been used by universities as diverse as Princeton and DeVry.

Know When to Hold ’em, Know When to Fold ’em