Hard to Read: Coding, with Communication

Cole Turner
Cole Turner
5 min read
Cole Turner
Hard to Read: Coding, with Communication

Have you ever opened your pull request and then received this comment?

This is hard to read, could you change this?

Source code is just like any other kind of language: it's a construct of symbols and grammar that make up meaning. When people say "that's hard to read," what they mean is one of a few things:

  • It's hard to make sense of the symbols.
  • It's not easy to follow the grammar.
  • This is not something I have seen before.

Source code is a diverse language just like any verbal language. We each have our own code dialect. In JavaScript, we do our best to normalize the inputs through tools like ESLint, Prettier, and TypeScript. They establish a common language of symbols, grammar, and sometimes a dictionary and thesaurus. And we make sense of it all, through the use of symbols and grammar in the code.

There's a great Cingular commercial from 2007, where a mom and her kid are communicating about their texting bill.

If you immediately recognize the symbol idkmybffjill, then you might not find that hard to read. On the other hand, if you've never seen that symbol before or you recognize some of the contents, you will find that symbol hard to read.

Let's look at some code examples.

// https://davidwalsh.name/javascript-debounce-function
function db(f, w, i) {
	let t;
	return function() {
		let c = this, args = arguments;
		let l = function() {
			t = null;
			if (!i) f.apply(c, args);
		const n = i && !t;
		t = setTimeout(l, w);
		if (n) func.apply(c, args);

This function is hard to read because all of the meaning has been obfuscated through obtuse variable names. All of the meaning is encoded in the implementation, without any content words. This confusion stems from a lack of recognition.

Did you guess that this function is a throttle? If so, then I'm sorry because I took this opportunity to trick you. This function is actually debounce.

I won't do that again! But I do want to share another example.

class Houseplant {
  // private fields
  #name = "Untitled";
  #species = "Unknown species";
  constructor(name, species) {
    this.#name = #name;
    this.#species = #species;
  // from upcoming tc39/proposal-record-tuple
  toRecord() {
    return #{
      name: this.name,
      species: this.species

In the example above, I am referencing some unusual syntax, from a new class properties functionality called Private Fields. The toRecord function also makes use of a current proposal for Records and Tuples.

Both instances are making use of the # sign to represent different functionality. This is confusing, and potentially hard to read because it lacks familiarity.

Confusion by Sense of Symbols is primarily an issue for folks who are new to the industry or new to the language. Due to a lack of recognition or a lack of familiarity, they might find themselves saying "that's hard to read."

Once a developer has become more familiar with a language, the struggle with code becomes a matter of grammar. It is the same struggle with learning any kind of verbal language, where the words start to make sense and the trouble becomes acclimating to the grammar.

Let's look at an example:

function pickColor() {
  return isTimeOfDay >= evening
    ? theme === 'light'
      ? Theme.colors.light
      : Theme.colors.dark || Theme.colors.default
    : theme === 'dark'
    ? Theme.colors.light
    : Theme.colors.dark || Theme.colors.default;

When the time of day evening, when the theme is light, what is the color?

For most people, it will take more than ten seconds to find the answer to that question. Most developers will say this code is hard to read and repeat a tactical answer about nesting ternaries, instead of highlighting the communicative issue at hand.

Confusion by Sense of Grammar is when the structure goes against the expected rules of grammar. What one developer thinks is "hard to read" is another developer's groove.

Some might say Brendan Eich, who in 1995 invented the scripting language Mocha that would later be known as JavaScript. Others might say TC39, the committee that oversees the evolution of the language today. That is a view on linguistics called prescriptivism.

But we know that a language, even for programming, is defined through its usage. Languages we have today evolved because someone made a change. This is a view called descriptivism.

"It's harder to read code than to write it"

—Joel Spolsky, Things You Should Never Do, Part I

If you're soloing it, then what's easy to read is whatever choices make you feel more productive. When you're a part of a team or a community - the code language is the sum of all the inputs, that which makes the group feel more productive and effective.

If it cannot be enforced with tooling or documentation, then it's a matter of preference. That is where tools like ESLint, Prettier, and Typescript excel at what they do: enforcing consistency. They keep developers in their lanes. The code language then becomes "what made sense when we configured our tooling." Preferences aren't usually helpful to leave as a comment to someone else's work unless it can be expressed in terms of semantic and cognitive weights for our uses of symbols and grammar.

Most developers only talk about familiarity. What matters is how much effort is spent mentally following the code. For example, abstractions (moving the symbols away) can lead to disproportionate indirection (distributing the grammar), making it harder to follow the code. Symbols can be used in place of grammar. All of this adds up to code language fluency. This kind of feedback is something we need to grow away from, and trust in tooling and documentation.

Be vulnerable and empathize. It's on us to try to understand and trust the coder who already did the work. As reviewers, we can describe the impact it has on the semantic and cognitive weights. Saying "that's hard to read" is no different than telling someone their verbal language is difficult to understand. When working with others, we have to understand that the common language is the lowest common denominator among the group, not the average familiarity.

Codebase language is an evolving negotiation.

Sometimes boilerplate is boilerplate. Other times it's keeping the semantic and cognitive weights balanced. It's important to understand that our familiarity with syntax, symbols, and grammar — isn't dictated by who wrote the code first. It is through the continued and evolving use amongst all using that codebase language.

Photo by Agence Olloweb.


More Stories

Why I Don't Like Take-Home Challenges

4 min read

If you want to work in tech, there's a chance you will encounter a take-home challenge at some point in your career. A take-home challenge is a project that you will build in your free time. In this post, I explain why I don't like take-home challenges.

Standing Out LOUDER in the Technical Interview

5 min read

If you want to stand out in the technical interview, you need to demonstrate not only your technical skills but also your communication and collaboration skills. You need to think LOUDER.

See more posts

Read it before anyone else. Subscribe to my newsletter for early access to the latest news in software engineering, web development, and more.