I once heard a team leader say that there are two types of code: ideal code and code that works in production. It was somehow meant to justify forgoing refactoring because “the code works, don’t touch it!”. My personal opinion is that he should be stripped of his team leading position and given a stern talking to.
The reality is that there are far more than two types of code. To name a few, there’s:
- ideal code that never makes it into production,
- ideal code that works in production
- good code that works in production
- bad code that works in production
- bad code that doesn’t work in production
and the worst of them all: bad code that pretends to work in production.
Just because it looks like everything is working fine does not mean that it really does and if your code is so unreadable that you can’t easily understand the logic in it, you should be worried.
Our job as programmers is not to write code that works at the moment. It’s to write code that works indefinitely (or as indefinitely as humanly possible) under all predictable circumstances. Yes, it’s true that ideal code usually stops being ideal once you start adding features – just like a house that someone lives in gets messier every day. However, just as with the house, we must do our best to regularly clean up the mess we make. Do the dishes, refactor that class, sweep the floor, rename that method, clean the windows, write some more tests…
A wonderful excuse for not refactoring code that I, sadly, hear fairly often is:
We can’t refactor our code! Something might break if we do!
The right response, of course, is that the discussed project must be in desperate need of writing new tests. And then refactoring. The usual comeback, of course, is:
But we don’t have time! We must ship new features!
I’m convinced that this is the most illogical conclusion one could ever come to. It’s like preparing for a party and saying that instead of cleaning the house you’ll buy more balloons because the guests are coming soon. Sure, they might be dazzled by the number of colorful, pretty balloons. No, they probably won’t notice that you didn’t sweep the dust from under the bed.
But the party won’t be so much fun when one of the guests slips on a banana peel you left on the floor three weeks ago and breaks their head on the cupboard you forgot to close.
It is simply irresponsible to focus on new features if you can’t be sure that existing ones are really, definitely working correctly and, moreover, won’t break if you add new ones (which happens often with spaghetti code).
The bottom line is, our obligation as programmers is to write code that works and we can’t ensure that it works unless we can actually read it. Furthermore, just because it seems to work in production doesn’t mean it really does.
Maintaining code readability is the least we can do and I would argue that even that is not enough to guarantee that the code works properly. After all, that’s what unit tests are for.