Thursday, September 16, 2010

Don't Save Bad Code

As coders we're often asked to do things we don't know how to. Use a new library, use a new microcontroller, try a new compiler. So we start to experiment - make something 'just to see' if you can program your microcontroller or figure out how a library works or do simple socket communication. Then you think 'Wow! That worked! Let's try more things! I'll just start coding everything here in main()....' And then you get eaten by raptors.

You've made a mistake. You weren't thinking about organizing this mess or even making sure it was robust - you were just seeing what you could do. Your main() is 1000 lines and you're not sure what all of this code is supposed to. You didn't comment because it was all so obvious earlier, but now all you know about this line is that if you comment it out the program crashes. Whoa boy.

But still you feel the compulsion to try to work with this mess. You want to massage it, start breaking things off into functions, add a few defines, rename the variables and make it work again. But step back - is this what you're supposed to be doing? After all, you started this to learn the library or a framework and, well, haven't you? Isn't that information tucked in your head by now? Is this code even remotely close to your final product? Could it be? Does it work? Is it well-organized and robust? Didn't you have a real development plan somewhere here? Complete with projections and unit tests? And haven't you learned enough to start on the real work, rather than just trying to save this one .c file out of sentimentality?

You shouldn't try to save old code just because it's already written. Old code has no particular value over newly-written code for several reasons.

First, there is no cost to writing new code in place of old code. When working with physical objects if you make a mistake with a material you have to pay to replace the material. If I were whittling wood and I shaved off a little too much I would try to work with it. But with software we have source control, so if I make a mistake I can revert it or even start over without losing anything. I still have access to the important things: my old code and the knowledge I've gained from writing that code.

Second, new code presents an opportunity for a more correct solution. So you've sat down and written a lot of bad code. You feel like deleting it would mean deleting the progress you've made towards a correct solution. But surely you haven't been asleep this whole time? Your mind has been watching the code you're writing and doing its own work - pondering the patterns behind the madness, taking note of vulnerabilities, seeing patterns that could become functions, etc. You've gained a lot of experience just by doing it wrong and at the very least you have a better idea of what NOT to do once you start over. A fresh start allows for a more pure expression of the solution that can't be shoehorned into your ill-conceived beginning.

Third, you will improve your tests by trying things different ways. If you use a FOR loop then you won't encounter errors that you might if you used a WHILE loop. If you're using dynamic memory allocation instead of static you have more things to worry about. Each new method of solving the problem before you opens up new areas for failure. Will this sub-function I just started using return 0x00 for any reason? Because I return its value without checking it and zero would not work well for a return value. So write a unit test to ensure that the return value is never 0. That's one more level of certainty that you're writing correct code, not just code.

Fourth, writing new code allows you to follow best practices more closely. So after a few hours of hacking together your test program-turned release candidate you decide to see what happens when you run splint on it. Turns out it produces so many warnings that your ego crawls into a hole and hides. What's the right course of action? Fix the warnings one by one until the splint output is clean. What do most people do? Turn off splint and wait for it all to be caught in integration. It's much easier to run splint on a 5-line function than 1000. Starting over gives you the chance to comment, run analysis tools, develop unit tests and format your masterpiece when it's a much more manageable size. This will only help you in the long run by front-loading much of the work that leads to clean, well-running and tested code at integration time.

Whenever you are writing - be it code, a short story, a joke, a play, a manual on annoying your cat remember this: the measure of how well you have written isn't the number of words - it's whether you accomplished what you wanted with those words. Eliminating incorrect code instead of massaging it back to life costs you nothing, lets you write more correct code, expands the range of your unit tests and helps you follow process more closely. So don't save that crufty old code, replace it and move on.

No comments: