technical: Maintaining Code That Sucks
It took me a week. 40 hours poring over a mere 500 lines of code. Just to find out what on earth the module did. And this code was terrible. No comments: NONE. No subroutines. If that wasn't bad enough, it used undocumented APIs. Terrible. Oh yes, and its performance was horrible: my job was to get it to run faster.
The Problem With Bad Code
We've all been there. Tasked with analysing or modifying some code that totally sucks. And when we do, we're faced with some hard choices.
I like clean code. I can't tell you how much I like clean code. So, my first instinct is to rewrite. Let's fix it while we can. Let's add error handling code, cut up that big program into subroutines, and get rid of Goto statements and other crimes against humanity. But this comes with risk. Any change could introduce a problem.
This also comes at a cost. Rather than spending an hour doing a small change and testing, we're spending several hours performing larger changes, with increased testing, code review, and other requirements.
The other option is to do the minimum changes, and leave everything else as is. But this also comes with risks.
If you don't fully understand what that bad code does, any changes you make have a higher risk. And understanding spaghetti code with no comments isn't easy. To be safe, you'll want spend a lot of time analysing that terrible code, confirming that you really understand what's happening. Try that when you're under time pressure.
Ideally, every program will have a suite of unit test cases that can be used to verify the operation. So, after all the changes, we compare the unit tests before and after changes, and compare. However, if a program has bad code, the chances are that it won't have great unit test cases, if any.
Changing bad code can increase risk and the cost of changes. But what do you do if you find an error in that bad code? If you're like me, you're already saying "fix it." But don't be too hasty. Some colleagues were working on a module used widely in an organization, and found that there was a bug. So, they fixed it while they were in there.
The problem was that many of the programs that used that module had their own code to 'tolerate' the bug. So, the new 'clean' module introduced its own problems.
So, we can choose to live with the bad code, or rewrite it. These are both extremes: are there any other options?
We could isolate that bad code: put it into a subroutine, and then write a 'nice' code wrapper that calls it. Or we could go the other way around, and isolate our 'good' code. This could be in a subroutine, or just within the code, but with great comments, error handling and the like.
We could 'front end' the bad code. Suppose we are adding new functionality. We could have some code at the beginning that jumps to our nice code if calling new options, or the bad code for old options.
We could also offer a choice to those calling our module by offering new parameters. If calling modules use the new parameters, they get our good code. Otherwise, they live with the bad code. This option could avoid the problem mentioned previously where calling programs had to code tolerate errors in a module they called.
When I'm wading through poorly documented code, I often add comments as I go along. This helps me understand what's happening, and can be left for others attempting to follow your trail.
What I Did
In the end, I rewrote the entire module: in a different programming language. I found a better way of doing what was needed, using published APIs that were so much faster. I added error handling logic, and lots and lots of comments. In my case, the program was relatively small, didn't accept many parameters, and obtained and returned information. So, I was confident (after 40 hours of analysis) that I knew everything it did, and my replacement program could do the same.
But faced with a different program, I'm not sure which way I'd jump. In younger days, I would have charged forward to rewrite the code. Today, I'm a bit more conservative, so may well try and live with the bad module. It depends on how big and complicated it is, how bad the code really is (is it just me?), how much time I have, and what else is happening around the module (lots of things changing, or not; lots of things calling it, or not)?