Human-Readable JavaScript: A Story of Two Experts

Everyone wants to be an expert. But what does that even mean? Over the years, I have come across two types of people called “experts.” An expert of the first type is a person who not only knows every cog in the language, but also uses all these cogs, regardless of whether it is beneficial. An expert of the second type also knows every syntactic subtlety, but is more selective in choosing a tool for solving a problem, taking into account a number of factors, both related and unrelated to the code.

Let’s guess what type of expert you would like to see on your team. Second, right? This is the kind of developer who strives to produce human-readable code, strings of JavaScript that other people can understand, and that are easy to maintain. But the characteristic “readable” is rarely defining — in fact, it is usually in the eye of the beholder. So where does this lead us? What should we strive for if our goal is readable code? Is there a clearly right or wrong choice in this case? Depends on a lot.

The obvious choice

To make the developer’s job easier, TC39 has added many new features to ECMAScript in recent years, including many proven patterns borrowed from other languages. One such innovation introduced in ES2019 is the method Array.prototype.flat ()… It takes a depth or Infinity argument and aligns the array. If there are no arguments, the default depth of the array is 1.

Before this addition appeared, the following syntax was required to align the array to a single level.

let arr = [1, 2, [3, 4]];
[].concat.apply([], arr);

// [1, 2, 3, 4]

By adding flat (), you can describe the same feature with just one expressive function.

arr.flat();

// [1, 2, 3, 4]

Is the second line of code easier to read? Absolutely yes. In fact, both experts would agree with this.

Not every developer is aware of the existence of flat (). But it is not necessary to know about this in advance, since flat () is an understandable verb, from which it is clear what is happening here. It is much more intuitive than concat.apply ().

This is the rare case in which you can confidently answer which syntax is better – new or old. Both experts, having familiarized themselves with the two syntaxes, will choose the second one. They will choose the line of code that is shorter, clearer, and easier to maintain.

But choice and compromise are not always so straightforward.

Check for lice

What is wonderful about JavaScript is its incredible versatility. This is why it is everywhere on the web. Is it good from your point of view or badly – already another question.

But such versatility brings with it the paradox of choice. The same code can be written in many different ways. How do you determine which one is “correct”? Such a solution cannot even be approached if you do not know all the available options and do not understand what they do not reach.

Let’s try functional programming with map () as an example. I’ll walk through a few iterations here – they’ll all lead to the same result.

Here’s the most concise version of all of our map () examples. It has the smallest number of characters, and they all fit on one line. We will build on this version.

const arr = [1, 2, 3];

let multipliedByTwo = arr.map(el => el * 2);

// multipliedByTwo равно [2, 4, 6]

The following example adds just two characters: parentheses. Have we lost anything? Have you bought it? Does the weather make it always necessary to use parentheses in a function that has more than one parameter? I think – yes, it does. There is nothing wrong with adding them here, but the code consistency will increase significantly when you inevitably have to write a function with many parameters. In fact, at the time of this writing, in Prettier this limitation turned out to be mandatory; there I was unable to create an arrow function without parentheses.

let multipliedByTwo = arr.map((el) => el * 2);

Go ahead. We’ve added curly braces and a return statement. This is now starting to sound like a traditional function definition. Right now, it might seem like a keyword as long as the entire logic of the function is firing a cannon at sparrows. But, if the function contains more than one line, then this additional syntax will definitely be needed. Are we assuming we won’t have any other functions longer than one line? Seems dubious.

let multipliedByTwo = arr.map((el) => {

  return el * 2;

});

Next, we removed the arrow function altogether. We use the same syntax as before, but now we prefer the function keyword. This is interesting because there is no scenario in which this syntax would not work; for any number of parameters or strings, we will have no problem, so here we are strong in uniformity. This code is longer than our first definition, but is it really that bad? How does this harm a new programmer or someone who is not skilled in JavaScript, but in some other language? Would anyone with a good knowledge of JavaScript be confused by this syntax when comparing?

let multipliedByTwo = arr.map(function(el) {

  return el * 2;

});

Finally, we come to the last option: pass only a function. And timesTwo can be written using any syntax we like. Again, there is no scenario in which passing the name of the function would turn out to be a problem for us. But let’s take a step back and think about whether such a code could confuse anyone. If you’re just getting started with this codebase, is it clear to you that timesTwo is a function, not an object? Definitely, map () will serve as a hint here, but such a detail can be missed. How about where timesTwo is declared and initialized? Is it easy to find it? Is it clear what it does and how it affects the result? All of these considerations are important.

const timesTwo = (el) => el * 2;

let multipliedByTwo = arr.map(timesTwo);

As you can see, there is no obvious answer here. But choosing the right options when building your code base is only possible if you understand all the options and their limitations. In particular, you know that you will need parentheses, curly braces, and return keywords to make your code consistent.

There are a number of issues to attend to when writing code. Questions and Answers productivity – as a rule, the most common. But when comparing functionally identical code fragments, then the choice should be made with an eye to the people who will have to read this code.

Maybe newer isn’t always better

So, we looked at a very pronounced example in which both experts would prefer the newer syntax, even if it is not well known. We’ve also looked at an example that asks a lot of questions but doesn’t provide many answers. Now let’s dive into the code I wrote earlier … and deleted. This code turned me into an expert of the first type when I solved the problem with a little-known syntactic construct, thereby neglecting my colleagues and the convenience of maintaining our code base.

Destructuring assignment allows you to unpack values ​​from objects (or arrays). It usually looks something like this.

const {node} = exampleObject;

Here, on one line, a variable is initialized and assigned a value. But that may not be the case.

let node

;({node} = exampleObject)

In the last line of code, the value is assigned to a variable using destructuring, but the variable is declared one line up. This is done often, but many do not realize that this is possible.

Let’s take a closer look at this code. The awkward semicolon is imposed here, and this is in code where the semicolon is not used to terminate lines. Here the command is enclosed in parentheses and curly braces are added; it is completely incomprehensible what is happening here. It is difficult to read this line, because as an expert I had absolutely no right to write such code.

let node

node = exampleObject.node

This code solves the problem. It works, it is clear what is being done in it, and my colleagues will understand it without looking anywhere. As for the destructuring syntax, I am not should apply it only because can apply.

Code isn’t everything

As we have seen, the solution of Expert-2 is seldom asked if we proceed only from the code; however, it is easy to discern what code each expert should write. The point is that machines must read the code, and people must interpret it. Therefore, you need to take into account factors that are not only related to the code!

Working on a JavaScript development team will have a different approach to code selection than working on a multilingual team, whose members are less immersed in linguistic subtleties.

Let’s compare the expansion operator and concat () as an example.

The spread operator was added to ECMAScript a few years ago and is now very common. This is a kind of helper syntax with which you can do a lot of things. In particular, concatenate a number of arrays.

const arr1 = [1, 2, 3];

const arr2 = [9, 11, 13];

const nums = [...arr1, ...arr2];

With all the potential of the extension operator, its symbol is not self-evident. So if you don’t know what it does, it won’t help you much. Whereas both experts are quite may assuming that the team of JavaScript professionals is familiar with this syntax, Expert-2 will probably wonder if the same can be said about the team of multilingual programmers. Therefore, Expert-2 may prefer the concat () method as it is an informative verb that can probably be understood from the context of the code.

This code snippet gives the same numeric result as the above example with the extension operator.

const arr1 = [1, 2, 3];

const arr2 = [9, 11, 13];

const nums = arr1.concat(arr2);

This is just one example that demonstrates how the human factor affects the choice of code. The codebase that people from different teams have access to may be governed by stricter standards that don’t necessarily keep up with all the cool syntactic innovations. Then you have to digress from the main source code and take into account other factors regarding your toolkit that can complicate or make life easier for people working on this code. There is code that can be structured so that it will don’t respond well to testing… There is a code that will drive you into a corner, and you cannot further scale or add new features… There is a code in which performance suffersare supported not all browsers or poor accessibility… Expert-2 takes into account all these factors in his recommendations.

Expert-2 also takes into account the naming factor. But let’s be honest, even experts in most cases, the naming is unsuccessful.

Conclusion

The real expert is not someone who applies every squeak from the specification, but someone who knows the specification well enough to rationally deploy syntax and make well-thought-out decisions. An expert who grows to this level can prepare new experts.

What does this mean for those of us who consider ourselves an expert or at least aspire to become an expert? This means that there are many questions to ask yourself when you write code. Adequately evaluate those developers who are your target audience. The best code you can write is code that solves some complex problem, but is by definition understandable to those who will read your codebase.

Yes, it is very difficult. And there is often no single answer. But you should be thinking about the above when you write each of your functions.


Our virtuals can be used for expert Javascript development.

Register using the link above or by clicking on the banner and get a 10% discount for the first month of renting a server of any configuration!

Similar Posts

Leave a Reply