There’s no such thing as bad code
Are you worried that you’re writing bad code? Code that doesn’t follow best practices, code without tests, code that violates coding standards, code you simply don’t want to think about because it’s so very very embarrassing?
In fact, there is no such thing as inherently bad code, or for that matter inherently good code. This doesn’t mean you shouldn’t be judging your code, it’s just that if you’re worrying about whether your code is “good” or “bad” then you’re worrying about the wrong thing.
In this post I will:
- Demonstrate that “bad” code can be “good” code under different circumstances, and that “best practices” aren’t.
- Suggest a handy mental exercise to help you move away from thinking in terms of “bad vs good”.
“Bad” code, “good” code
Let’s look at a couple of examples of “bad” code and see that, under some circumstances, this “badness” is irrelevant.
As everyone knows, “good” code is easy to read and follow. You need to choose readable variables, clear function names, and so on and so forth. But then again—
- If you’re writing code for fun, for your own use, who cares? You are the only one who will ever read this code, so you can choose whatever system makes sense to you so long as you can it understand.
- If you’re entering the The International Obfuscated C Code Contest, you have a completely different set of criteria. There are some truly amazing entries in past contests; calling them “bad code” is just wrong.
- If the value your code provided to your organization is sufficiently higher than the cost of maintenance, it can’t really be said to be “bad” code. The world is full of spreadsheets and one-off scripts that are hard-to-read, but nonetheless work correctly and produce huge value.
As everyone knows, “good” code has unit tests, and “bad” code does not. But then again—
- You’re writing a packaging utility for your product. You have end-to-end tests that make sure the resulting packages install correctly. Unit tests are a waste of time: they prove no additional correctness, and your packaging tool is not your product, it’s just a utility whose code will never be shared elsewhere.
- You’re building an exploratory prototype. Once you’ve figured out if this idea works you’ll be throwing the code away away and starting from scratch. Why write unit tests?
There’s no such thing as “best practices”
At this point you might be getting a little annoyed. “Yes,” you might say, “these are some exceptions, but for the most part there are standard best practices that everyone can and should follow.” Consider:
- Formal verification is a practical and useful technique for finding bugs in your code. See for example how AWS uses formal verification to validate their services.
- NASA’s software development practices are amazing, both effective and expensive. One quote to give you a flavor: “The specs for the [shuttle] program fill 30 volumes and run 40,000 pages.”
Both NASA’s techniques and formal verification lead to far less defects. So should they be best practices? It depends: if you’re building the website for a local chain of restaurants, using these techniques is ridiculous. You’ll ship a year late and 10× over budget. On the other hand, if you’re writing the software for a heart monitor, or a spacecraft that’s going to Pluto, “I wrote some unit tests!” is very definitely not best practices.
A sanity check for your code
Instead of feeling embarrassed about your “bad” code, or proud of your “good” code, you should judge your code by how well it succeeds at achieving your goals. Whether you’re competing in the International Obfuscated C Code Contest or working on NASA’s latest mission, you need to use techniques suitable to your particular situation and goal. Judging one’s code can be a little tricky, of course: it’s easy to miss the ways in which you’ve failed, easy to underestimate the ways in which you’ve succeeded.
So try this exercise to give yourself some perspective: every time you write some code figure out the tradeoffs you’re making. That is, identify the circumstances and goals for which your current practices are insufficient, and those for which your current practices are overkill. If you can’t come up with an answer, if your code seems suitable for all situations, that is a danger sign: there’s always a tradeoff, even if you can’t see it.
Finally, when you encounter someone else’s code, be kind: don’t tell them their code is “bad”. Instead, go through the same exercise with them. Figure out their goals, and then walk through the tradeoffs involved in how they’ve written their code. This is a far more useful way of improving their code, and can help you understand why you make the decisions you do.