CodeTips#2: Clean code in JavaScript

A beginner's approach

INTRODUCTION

INTRODUCTION

I believe the comic strip above sums it well: You write clean code mainly to understand yourself in the future. Not only that, you write clean code as a way of passing that way of thinking forward, which will facilitate the job of other developers who read your code. Another way of thinking about it is: You will never write more code than you will read. You will read other people’s code and read your own, beyond writing it. So, the more time we spend making our code readable, the better.

As Ryan McDermott insightfully points out, the craft of Software Engineering is just a bit over 50 years old. Maybe when software architecture is as old as architecture itself is today, there will be rules and set-in-stone principles. For now, these topics, which we will get into here, are more like guidelines. It’s more important to grasp the idea of clean code than to follow these rules blindly. Remember every line of code begins as drafts of a train of thought. Although it should be clean, it could turn cleaner as time goes on. That’s okay.

We will focus on JavaScript in our examples of good and bad code, as this series of posts is meant to be focused on one specific technology. But, going forward, try to remember: these guidelines work for any language.

VARIABLES

MEANING

Your variables should have meaning, so anyone who reads their name gets a clue of what they are. For instance, let’s say you are getting the current weight of someone, to compare it with their weight at the beginning of some project. You could call these:

const weight1 = 85;
const weight2 = 92;

For anyone reading these, they could infer weight1 is from sometime in the past, but they have no way of knowing if weight2 is the current weight or even some other weight who knows from when. In this case, it would be more intelligible to call them:

const weightBeginning = 85;
const weightCurrent = 92;

Now, just by a fast glance at these two lines, we know one of them stores the weight of someone at the beginning, while the other one is the current weight.

CONSISTENCY

Consider a project where you need to store colors in different parts and files of your code. Instead of saving them with different names along code lines and files, be consistent: Use the same name wherever you can.

const headerColor = "black"
const colorOfFont = "white"

Even though these variable names have meaning, it would be easier to read the code surrounding them if they followed a pattern:

const headerColor = "black"
const fontColor = "white"

NO MAGIC NUMBERS

You don’t want to stop reading some code, in the middle of trying to understand it, just because you don’t know where some number came from. That’s where the idea of removing magic numbers comes from. Instead of:

const secondsInOneHour = 60 * 60

Consider attributing these numbers to constants easily found in your code:

const MINUTES_IN_ONE_HOUR = 60
const SECONDS_IN_ONE_MINUTE = 60
const secondsInOneHour = MINUTES_IN_ONE_HOUR * SECONDS_IN_ONE_MINUTE

GO STEP BY STEP – USE MORE VARIABLES

There’s no need to rush the reader of your code, nor are you gaining anything shoving lots of parameters and functions inside parenthesis.

What I mean by that is this is bad:

expect(screen.getByRole(‘button’, { name: 'Cart' })).tobeInTheDocument()

And this is good:

const cartButton = screen.getByRole(‘button’, { name: 'Cart' })
expect(cartButton).toBeIntheDocument();

See? Steps.

BE EXPLICIT

Wouldn’t it be nice if your code read like plain text? To achieve that, variables must be explicit in their naming.

More people do this than you imagine:

const arrayOfNumbers = [1, 2, 3]
const doubleArrayOfNumbers = arrayOfNumbers.map(e => e * 2)

Prefer this:

const arrayOfNumbers = [1, 2, 3]
const doubleArrayOfNumbers = arrayOfNumbers.map(number => number * 2)

BE DRY

At the same time, we should be explicit why repeat ourselves? We must find the balance between these approaches. Take these examples:

Why repeat the card three times,

const card = { cardSuit: 'clubs', cardValue: 'A' }

If one would suffice?

const card = { suit: 'clubs', value: 'A' }

FUNCTIONS

PARAMETERS

It would be best if you kept it to 2 or less. That would make your function easier to read and to test. If you must have more than two, consider passing them as objects! That way you will be sure to be passing the right arguments, which will also make testing easier.

In this example, we have to pay attention to the specific order of the parameters and what they are:

function noObject(name, surname) {
  console.log(name)
  console.log(surname)
}
noObject('John', 'Doe') // John Doe

But for this next example, things get a little more straightforward and explicit:

function noObject({ name, surname }) {
  console.log(name)
  console.log(surname)
}
noObject({ name: 'John', surname: 'Doe' }) // John Doe

FUNCTIONS SHOULD DO ONE THING

Functions should do one thing and do it well. This makes me think of the funny acronym I once heard in an episode of The Office: KISS – Keep It Simple Stupid.

For example: If your function checks if a user is active and, if so, it does something to it, that’s two things. Build one to find active users and another to do something with each of the active users.

Another example would be an if/else statement inside your function. If it has one, maybe breaking these two cases into two functions is a good idea.

IMPERATIVE NAMES

It shouldn’t be hard to tell exactly what your function does just by reading its name. That’s why you should name your functions based on what they do like you were telling them to do it.

Does your function add the product to a cart? Name it addProductToCart, not addToCart or cartFiller.

Be precise in naming.

BEWARE OF SIDE EFFECTS

Side effects are anything your function may do that it wasn’t designed to. For example, the following code alters the original testString array.

let testString= "John Doe";
function splitString() {
  testString = testString.split(" ");
}
splitString();
console.log(testString); // ['John', 'Doe'];

A safer way to do this would be to “copy” testString into a new variable and altering that variable. Like so:

let testString= "John Doe";
function splitString() {
  const newString = testString;
  return newString.split(" ");
}
splitString();
console.log(testString); // 'John Doe';

Wrapping everything: Be careful not to alter something outside of the function that should not be.

MORE

We still need to talk about SOLID, testing, error handling, formatting, and comments. Stay tuned!

Can’t wait?
Here are a few links to take you deeper:

We want to work with you. Check out our "What We Do" section!