The Myth Of The Good Practice

In this post, I will discuss the idea of "good code" and "good practice" widespread in the programming community. What do people mean when they say some code is good, what makes a piece of code good, and, finally - good code "for whom", best practice "for whom". Who exactly is the supposed receiver of the "goodness" quality we assign to this code?

You hear it here and there - good code, bad code, bad practice, best practice.
You can google it up. It depends on the language, it depends on the framework, it depends on the year. "Python best practices", "Ruby on Rails best practices", "Js best practices 2022".

I hear "best practice", and I think "best for whom?". Let's dive into this question, but, prior to that, let's take a look at the arguments used to make the claim about the quality of the code in the first place.

Scrambling through my memories, I congealed all arguments programmers use to declare a piece of code "bad" into 3 major categories:

  • Worried about the user
    "This code makes it worse for the end user of the program."
    • Inefficient
      Heavy on server (RAM intensive, CPU intensive), or heavy on the user (slow UI).
    • Wrong
      Doesn't work the way the programmer thinks it works; or it does work the way the programmer thinks it works, but it shouldn't.
    • Unsafe
      Lousy security for user data, or for the server resources.
  • Worried about the programmer
    "This code makes it worse for the programmers interacting with the code - reading it or supporting it."
    • Hard to understand
    • Hard to update
  • Just so
    "This is a bad practice."
    • By authority
      "That's how we did it at Google."
    • By majority
      "It's a common practice to do it this way."
    • "The X way"
      "Why shouldn't we have methods longer than 5 lines? This is The Rails Way."
      "Why don't we want a comfy alias for this function name? There should be one and only one way to do something, it's in The Zen of Python."

I'd like to draw your attention to the fact that this is not a list of reasons a programmer might criticize a piece of code, this is a list of arguments used while criticizing the code. A programmer might simply want to learn some cute language because they like it, or because it makes them more competitive on the market - and this might be the reason for deeming the old codebase "bad", but it won't be the argument used for proving it's bad.

You shouldn't take these arguments at face value either - if someone says "it's a common practice to use snake_case in Python" (a seeming majority argument), it might well be a shortcut for saying "consistent casing makes it easier to understand the code for the programmers", which is a thinly concealed "worried about the programmer" argument, it's best to follow the principle of charity.


What's interesting about this division is the following.

All arguments from the "Worried about the user" category are objective - it's possible to check which code is comparatively more efficient, and it's straightforward to check whether the code is wrong or compromises security.

It's also easy to fact check the arguments from the "Just so" category - these are bad arguments, and I'd argue you shouldn't use them, but the person using this argument is usually able to source their claims, whether that helps the structure of the argument or not.

I believe the ease of proof is one of the reasons the arguments from either of these categories are used overwhelmingly frequently, even when the real complaint might lie in the second category ("Worried about the programmer" category, where the concern lies in how hard it will be for a programmer to understand or update the code).

For example, if it's hard for you to read nested code (in CSS, for example), you are unlikely to say so directly - you will search for the arguments against nested code that sound more objective, that are easier to verify, and that don't paint you in bad light (it's embarrassing to be admitting to having a hard time understanding code).

You might say that highly nested CSS is inefficient (and you'd be right, in case with CSS, for example, deep nesting might make the CSS file heftier), and that would be the objective claim from the "Worried about the user" category. You might refer to the SASS style guide that advises no more than 3 levels of nesting, and that would be the objective claim from the "Just so" category.
If you are to use the argument from the "Worried about the programmer" category, and genuinely say "this code is hard to understand", however, - then you would only be able to provide the evidence of your own word - this code is hard to understand for you.

So you're worried about the programmer, huh?

The "Worried about the user" arguments (Inefficient/wrong/unsafe code) are objective and frequently strong, but easily fixed.
The "Just so" arguments are, in the best-case scenario, a concealed appeal to consistency, which is in the realm of "hard to understand for a programmer" subcategory, or, in the worst-case scenario, a not particularly interesting or strong argument from authority.
The arguments from the "Worried about the programmer" category are underused, as they are harder to source due to very little research on the topic, and more vague, as we usually leave the "programmer" in question unidentified, - however, "ease of understanding" and "ease of manipulation" is what constitutes code quality in its classic sense, making it the most interesting category to explore.

Let's consider some reasons the code might be hard to understand:

  • Code introduces too many variables to keep in your working memory
  • Functions call each other in a way that requires you to track multiple threads in your head
  • Variable names that don't indicate what's kept in them
  • Function names are very long, making it harder to read the code

Notice how these arguments, unlike the arguments from the other categories, rest in the interaction of the code with the brain of the programmer. It is clear each of these is subjective - people have vastly varying abilities when it comes to holding a number of variables in their mind - we all have a different working memory, and we all have a different long-term memory.

"Convention over configuration" design paradigm might be more embraced by someone who finds it easier to memorize the said convention. Shorter function names are embraced by people who get easily distracted by too much text. There is a real neurological difference between people for whom attention is a scarce resource, and people for whom long-term memory is a scarce resource. This makes every "this code is hard to understand" argument subject to the following question: hard to understand for whom?

Hard to understand for whom

So when we say some code is hard to understand, who do we have in mind as an attempted reader? Let's consider a few possibilities.
The code might be hard to understand:

  • For the programmer themselves
  • For the team working on this project
  • For the team, and for the programmers who might join this team in the future
  • For most programmers in the world
  • For most programmers sufficiently competent in this language
  • For programmers above a particular intelligence threshold

It might seem obvious that understandability of the codebase is a business question, and the right answer is simply "whatever option is best for this company", which typically translates into "code must be easy to understand for the team working on this project". However, the team working on the codebase is not a constant - we might demand a greater baseline competence from our coworkers, and we might demand a greater intelligence from the new hires. Furthermore, when it comes to open source, we never know who might want to contribute - we have to make a decision on who would be able to understand our code prior to those people joining as contributors.

We saw a switch from solutions that focus on competence to solutions based on inclusivity in recent years. At the same time, we saw a switch from object-oriented programming to functional programming favoritism in recent years - many OOP practices are outright considered bad practices at the moment. This has been explained, partially, by the greater mental load that OOP demands from the programmer - the necessity to keep track of the mutable state of the program in your head.

I believe this switch to be at least partially explainable by the cultural update - we don't believe that programming is reserved for the best of us anymore, it has become a commonplace profession. To give you a sense of what it was like a few years ago, consider the blog post "Skill Disparities In Programming" by Jeff Atwood, the founder of StackOverflow, where he states there are huge disparities in programmer ability, and you should seek other professions if you are not in the top percentiles of coding ability. Compare this to the more recent "Learn to code" movement. Coding turned from the profession you have to fight for, to the profession that most people can do, and find themselves required to do. Correspondingly, our sense of "who should be able to understand this program" expanded, dragging our understanding of what is and isn't a good practice along with this expansion.

Conclusion

I believe the democratization of the coding culture to be a step in the right direction. The notion that most programmers find FP to be less mentally demanding than OOP has never been checked, however - we just converged (and I believe temporarily) on this idea as an industry. Dynamic, collective wisdom might work better than studies on the topic, however we shouldn't lose sight of individual differences that don't reside on a single axis of the mysterious "programming ability". The fact that different people might be better suited to different practices is completely absent from the zeitgeist, and I want us to be more cognizant of this phenomenon.

Next time you feel compelled to say something is a bad practice, consider specifying exactly why this code is harmful, and, if your claim is that this code is hard to understand, consider for whom this code is hard to understand.

Finally, avoid thinking of programmers as residing on a single axis of competency or a single axis of intelligence. Consider for whom exactly this piece of code might be hard to read - for people who have a limited working memory? For people who have a limited long-term memory? For people who rely on the geometric layout of the code to parse it visually? For people who lose track of the flow of the program if they have to read a long variable name?

It might be impossible to check the level of competency of the people who will have to read your code, and it might be even harder to predict their cognitive specs, however it's important to keep in mind as a defining factor for whether the code is understandable, and it's infinitely better than pretending that the current fashion is objectively the best fashion for each and every one of us, the swamp we repeatedly drown in, likely due to the illusion of coding being an exact science.

What might be more understandable code for some people, might be less understandable for others. What might be the best practice for you, might be the worst practice for someone else.