Good code is not subjective

A spectre is haunting the software industry the spectre of maintainable code. Everyone of us has probably encountered shoddy code in our career as software developers. Everyone of us probably have an idea of what good code is. We, as software developers, are also very good at complaining about shoddy code in software whenever we stumble upon it. But do we write good code ourselves? In theory, we all believe that good code is important for the software. In practice, however, it’s a different story. We can point out design deficiencies in everyone else’s code, but whenever our own is judged by the eyes of other software developers, we usually resort to pointing at the subjectivity of what makes code good or not; “What is readable to you, may not be readable to me.”; “It all comes down to different styles in the end.”

In some arbitrary context, there may be an ounce of truth in those arguments. Code that is readable by me, may not be readable to you. However, I am also biased; of course I think my code is readable. I have intimate knowledge of what problem it is solving and how it works. Everyone else doesn’t. Therefore, whether something is readable or not should not be decided upon by the author of the code, but by the rest of the team (and the author, 6 months down the line).

Those arguments also seem to be the knee-jerk reaction to any form of criticism our own code may meet. It seems as though there is something common with those kinds of arguments: they all seem to boil down to “style” and “personal preference”; good code follows the team’s style guide; good code is subjective. If good code is subjective, then it must also be true that all code is neither better nor worse than any other code. Why is it, then, that we make the distinction between “good” and “bad” code? Is it good or bad in the same way spaghetti tastes good or bad to some people? And if so, why even discuss it? Why refactor something to make it “better”? Because if it’s subjective, what do we have to gain from it other than making the code “tastier” for ourselves? That’s a waste of time, isn’t it? Why bother?

Because good code is not subjective.

All too often when we discuss code quality, we fixate on aesthetics and non-issues, such as the placement of braces; snake_case vs. camelCase; tabs vs. spaces; number of lines in a method; length of method names and all various kinds of formatting. Now, don’t get me wrong, the code style is important as well, but the only thing that is important about it is the fact that the team should agree on a given code style and follow it. The code style isn’t what minimizes the frequency of bugs; it doesn’t prevent leaky abstractions; it doesn’t prevent duplicate code; it doesn’t help you convey the intention of your algorithms. What the code style does have however, is very little impact on maintainability of software overall.

Maintainability – Not Aesthetics

Maybe you’ve already managed to guess what point I am trying to make; namely, good code is about maintainability of the software. How does the code contribute to the maintenance costs of the software? This question should be the soul of the discussion. You could argue that it’s subjective whether good code is about aesthetics or maintainability, which is certainly true. However, how practical is it to define good code based on mere aesthetics? This doesn’t help the team direct its attention towards what is important. Rather, it makes it easier for us to disregard code smells on the grounds of personal preference. Aesthetics, as I mentioned before, have very little impact on the maintenance costs. In the end, if you’re looking to manage your technical debt, clearly it isn’t aesthetics that should be the main topic to be discussed; rather, the maintainability of the code should.

So, the remaining question is, can maintainability itself be subjective? There are various factors that influence the maintainability of the software (which I’ve discussed before) and those are objective by nature. If I tell you that your module isn’t testable since it’s tightly coupled to a remote SQL database in such a way that I cannot replace it with a mock; that’s not my opinion, it’s a fact. That fact also implies that network connection is needed for the test, that I have to potentially write test data into a real database and that the test may be slow to run. This may force me to compromise when writing the test, such as not testing functionality that involves reading data from the database, which in turn decreases code coverage. Some part of the module may not even be testable at all. And so on.

However, what may be subjective is the question of what is good enough.

What is good enough?

Since you can objectively point out deficiencies in some arbitrary code’s design, maintainability in itself isn’t subjective. Nor should it be. But what about deciding what is good enough? Surely we shouldn’t strive for perfection? That is true. However, what is good enough should be based on the objective truths regarding the code’s maintainability weighed against the investment required to mitigate the maintenance costs. It should not be based on aesthetics or non-issues such as those I discussed before. It should be a sound decision, a decision that doesn’t disregard the maintenance costs the code may contribute to. Even though it’s up to the programmer to judge whether something is good enough or not, it doesn’t mean that the judgment is subjective; that any judgment is neither better nor worse than any other judgment. We can make bad decisions. We can make good decisions. Whether they are good or not should be based on their potential mitigation of maintenance costs versus investment cost.

Summary

The key point to take away from this article is that whether code is good or not should be based on reality. What are we doing? We’re developing software for someone. We want to cut down development costs while maximizing customer satisfaction. We also want to be competitive; our software should be the best of its kind, by quickly adapting the software to satisfy changing requirements and delivering on time. Disregard for code quality will quickly increase maintenance costs while increasing time to delivery, preventing us from adding all rad features the customer may wish to have; increasing the frequency of bugs which in turn decreases customer satisfaction, and hindering our ability to compete with other software of the same kind.

Something is good enough when we’ve weighed our business situation against the maintenance costs and decided that the costs do not outweigh the investment required to make it better. This decision may be good or bad, and unfortunately, more often than not, it is decided that it is good enough when in reality it isn’t. Incompetence is a big factor, as well as the attitude toward what makes code good or bad. A software developer can erroneously decide that the investment cost to fix a certain deficiency outweighs its maintenance costs, and the problem is that this happens frequently based on the idea that good code is subjective; “it’s a matter of preference”. It is not. Nor should it be. This goes the other way around as well; ambitious software developers may erroneously decide the deficiency’s maintenance costs outweighs the investment cost to fix it. Those two cases represent two extremes that are far too common in the software industry; either complete disregard for the maintenance costs or complete disregard for the investment required to fix it. Of course, even if we pay equal attention to investment cost and maintenance cost, we may make the bad decision anyway. That’s life. The point I want to make is that we should aim to meet halfway, not to lean towards either extreme.