Categories
JavaScript Best Practices

JavaScript Clean Code — Function Arguments

Functions are important parts of JavaScript programs. They are used for dividing code up into reusable chunks. Therefore in order to have clean JavaScript code, we have to have easy to understand functions.

In this article, we’ll look at more characteristics of good functions, including reducing the use of switch statements, reducing the number of parameters in our functions, and getting arguments in an easy to understand way.

Switch Statements

switch statements are always long even if they have only a few cases. Therefore, they should be used in a way that’s never repeated by separating into a function that can be invoked wherever is needed.

An example of a bad switch statement would be something like the following code:

const calculatePay = (employeeType) => {
  switch (employeeType) {
    case 'PART_TIME':
      return calculatePartTimePay();
    case 'FULL_TIME':
      return calculateFullTimePay();
    case 'COMMISSIONED':
      return calculateContracorPay();
    default:
      return 0;
  }
}

The code above is long and it does more than one thing. It’s better to just skip the switch statement and then call them wherever they’re needed.

Also, we have to change this function every time we change the type.

Another problem is that we’ll have many other functions with this structure, which means the code will get longer and have more functions with the same problems as this one.

To deal with this, we can return the correct object for the type of employee that we want. For example, we can write instead:

const getEmployeeObjectByType = (employeeType) => {
  switch (employeeType) {
    case PART_TIME:
      return new PartTimeEmployee();
    case FULL_TIME:
      return new FullTimeEmployee();
    case CONTRACTOR:
      return new ComissionedEmployee();
    default:
      return undefined;
  }
}

And then we can just call this function to get the right type of employee and then do what we want with it.

It’s much better than having multiple functions with the same switch statement cases doing different things.

Descriptive Names

Like any other identifier in our code, the names in functions should be descriptive like everything else.

They should be named with names that tell what they mean. Information like the meaning, intent, and action should be conveyed from the code.

The type may also be helpful in some cases if the type of something isn’t immediately clear from looking at the code since JavaScript is a dynamically typed language.

Longer names are OK is they’re needed to convey all the information that we need to know. They can still be easy to read if they follow JavaScript’s convention, which is to use camel case for variables and non-constructor functions, upper case for constants, and upper camel case for constructor functions.

Function Arguments

Functions should have as few parameters as possible. This is because we have to look at all of them and when we call them, we have to make sure that they’re all passed in.

Therefore, if we don’t need parameters, then we shouldn’t include them. Zero is better than one and one is better than 2.

More than 5 is probably too much unless there’s some special reason for it.

Passing in arguments is hard since we have to gather all the variables and values and pass them in. Likewise, it makes testing harder for the same reason.

When we test a function with lots of arguments, we’ve to test all the possible combinations of arguments to get full test coverage. This makes testing a function that takes lots of arguments many times harder than ones that take fewer arguments.

Also, with the rest operator being a standard feature in JavaScript, it’s time to abandon the arguments object if we need to define a function with lots of arguments. It’s an array-like object so it has some property of arrays, like indexes and the length property.

We can also loop through it with a for or a while loop. However, it doesn’t have any array methods that are part of a normal array.

Like other array-like objects, it can be spread with the spread operator.

They just create confusion for lots of people since many people aren’t familiar with the arguments object.

The arguments object also doesn’t bind to arrow functions, so we can’t use it with them.

With the rest operator, we get an array of arguments returned from it, so we can do everything that we can do with arrays.

So instead of writing:

function add() {
  return [...arguments].reduce((a, b) => a + b, 0);
}

We should write:

function add(...args) {
  return args.reduce((a, b) => a + b, 0);
}

To make a function that adds some numbers together. Or better yet:

const add = (...args) => {
  return args.reduce((a, b) => a + b, 0);
}

since we aren’t using this in our code.

As we can see, the 2 later examples with the rest operator are much clearer than the first one. The first one has no parameters in the signature and yet we’re getting the arguments passed in from the arguments object.

The other 2 shows that our add function actually takes arguments and we’re actually doing something with them.

Conclusion

Functions might look easy to define on the surface, but to define functions that are easy to read and change, we have to do it with care. We should avoid having too many switch statements.

If we do need them, we should abstract them so that one switch statement can be used in multiple places. They’re long and should be minimized.

Also, the names of things inside a function should be descriptive, and we should never take too many arguments in our functions. If we do, use the rest operator instead of the arguments object to get the arguments passed into our functions.

Categories
JavaScript Best Practices

JavaScript Clean Code — Function Parameters and Side Effects

Functions are an important part of JavaScript programs. They’re used for dividing code up into reusable chunks. Therefore in order to have clean JavaScript code, we need to have easy to understand functions.

In this article, we’ll look at more properties of good functions, including flag arguments, dyadic and triadic functions, and side effects.

Flag Arguments

Boolean parameters should be used sparingly. It makes the function signature more complex and it tells us that the function does more than one thing (has multiple paths).

Dyadic Functions

Dyadic functions are harder to understand than functions that take fewer arguments. However, sometimes they make sense. For example, if we have an object that holds the Cartesian coordinate, then it should take 2 arguments.

For example, we can have a class with a constructor that takes 2 arguments as follows:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

const point = new Point(1, 2);

It’s pretty much impossible to define it any other way.

However, we do have to be aware that it takes more time and brainpower than functions that take fewer arguments.

Triadic Function

Functions with 3 arguments take a lot of time and brainpower to understand that functions that take 2 arguments.

There’re many more combinations of arguments to think about that if there’re 2 or fewer arguments.

Combining Arguments into Objects

If a function takes many arguments, we should consider combining them into objects.

This is especially true if they’re related. For example, the following function takes many parameters:

const describeFruit = (color, name, size, price, numSeeds, type) => {
  return `${fruitName} is ${fruitColor}. It's ${fruitSize}. It costs ${price}. It has ${numSeeds}. The type if ${type}`;
}

6 parameters is probably too many. We can clean this up by passing in an object instead:

const describeFruit = (fruit) => {
  return `${fruit.name} is ${fruit.color}. It's ${fruit.size}. It costs ${fruit.price}. It has ${fruit.numSeeds}. The type if ${fruit.type}`;
}

As we can see, it’s much cleaner. We don’t have to worry about passing in many arguments.

It also fits better on the screen since it’s shorter.

5 parameters are probably the maximum that should be in a function.

Verbs and Keywords

It’s a good idea to include verbs and keywords into our function names since they do something, which means an action word in the name is justified.

Also, we need to know what things we’re applying the action to. This means that we have to add some keywords to do that.

For example, a good function definition that meets this rule would be something like:

const copyArray = (array) => [...array];

The copyArray name let us know that our function makes a copy of an array.

It also lets us know what we’re passing into the function, and that’s clearly an array.

No Side Effects

Side effects are code in a function that makes changes to things that are outside the function.

This isn’t good because it’s making hidden changes to things that are outside the function.

We should avoid this as much as possible since it does something unexpected and also it takes testing harder because, in addition to taking in arguments, doing things and returning a result, it also makes changes to something outside the function that we have to account for.

This means that we have to test things outside of what the function returns.

For example, if we have:

let numFruits = 1;
const addFruit = () => {
  numFruits++;
}

const removeFruit = () => {
  numFruits--;
}

Then we have 2 functions with side effects because they’re both changing the numFruits variable which is outside each function.

A better way to write these functions is to write them as pure functions. Pure functions are functions that returns the same thing given the same arguments are passed in. Also, it has no side effects.

Pure functions are easier to test because of that and the behavior of them is also predictable.

We can rewrite the code above by writing them as follows:

let numFruits = 1;
const addFruit = (numberOfFruits) => numberOfFruits + 1;
const removeFruit = (numberOfFruits) => numberOfFruits - 1;

numFruits = addFruit(numFruits);
numFruits = removeFruit(numFruits);

We now have 2 functions that take in one numFruits parameter and return a number that’s one bigger or smaller respectively.

Then we can use them to change the numFruits variable that we have outside the functions.

As we can see, they do nothing to the numFruits but rather it returns the numberOfFruits parameter plus 1 or minus 1 respectively.

If we write tests for them, then we can test them easily by passing in the input and checking if the output is what we want. This is much better than committing side effects to a variable that might be available to the test code.

Conclusion

Flag arguments should be minimized. They tell us that the function can do more than one thing and it’s another parameter in the function signature.

Functions that take fewer arguments are better than the one that takes more. If it takes lots of arguments, consider combining them into a single object.

Finally, side effects should be avoided if possible. Functions with side effects do hidden things and are hard to test. Pure functions are much more testable and more predictable since they don’t commit side effects.

Categories
JavaScript Best Practices

JavaScript Clean Code — Function Exceptions and Duplicate Code

Functions are important parts of JavaScript programs. They are used for dividing code up into reusable chunks that does one thing mostly.

Therefore in order to have clean JavaScript code, we have to have easy to understand functions.

In this article, we’ll look at more parts of a function including output arguments, command query separation., throwing exceptions, and duplicated code.

Output Arguments

Output arguments are arguments that are accepted by a function that’s directly returned by the function.

This is weird since arguments are usually interpreted as inputs rather than being directly used as outputs. There aren’t many use cases for this. Usually, the arguments are computed in some way by combining them or checking them and returning the results by these checks and operations.

In JavaScript, if we want to change a shared state, we can put shared as state as class members and then we can have methods to manipulate the class members.

So we should write something like:

class FruitStand {
  constructor(numFruits) {
    this.numFruits = numFruits;
  }

  addFruit() {
    this.numFruits++;
  }

  removeFruit(numFruits) {
    this.numFruits--;
  }
}

rather than returning arguments that are passed in.

Command Query Separation

A function should either change the state of an object, or it should return some information about an object. However, it shouldn’t do both.

For example, we shouldn’t have a function like the following:

const setProperty = (obj, property, value) => {
  obj[property] = value;
  return value;
}

The function changes the obj object in place and also returns the value.

It does 2 things, which isn’t good, and the name doesn’t convey that also returns some information about the object. This misleads the user of this function when he or she didn’t read the function definition and just goes by the name.

Therefore, it’s better to separate the property setting and returning the value as follows:

const setProperty = (obj, property, value) => {
  obj[property] = value;
}

const getProperty = (obj, property) => {
  return obj[property];
}

It’s just much better to have functions that do one thing each like we have above so people don’t get confused about what they’re doing.

Throwing Exceptions in Better than Returning Error Codes

Returning error codes violate the command and query separation rule that we mentioned above. This is because the function that returns something returns it when an error occurs and returns something else when the function runs successfully.

This means the function both do something, which forms the command part and returns something, which forms the query part.

It should only do one or the other. Since the main purpose of the function is to do something rather than return something, so it should just do the command part rather than also returning an error code.

This means instead of writing like the following:

const setProperty = (obj, property, value) => {
  obj[property] = value;
  if (!value) {
    return 'Value not found';
  }
}

We should instead throw an exception as follows:

const setProperty = (obj, property, value) => {
  if (!value) {
    throw new Error('Value not found');
  }
  obj[property] = value;
}

Then we can catch and handle it if we need to:

try {
  let obj = {};
  setProperty(obj, 'foo', null)
} catch (ex) {
  console.log(ex);
}

We can eliminate lots of error code checking conditional statements with try...catch instead of having an if statement to check each error code returned by a function.

Extract Try…Catch Blocks

try blocks shouldn’t contain normal processing code. This is because it makes it confusing as to where we’re expecting errors to be thrown.

Instead, we should just wrap the code that we expect to have errors thrown inside a try block. Then we can write a catch block right below it to catch the exception.

For example, if we have normal code and code where exceptions need to be caught, then we can write the code as follows:

const functionThatThrowsError = () => { //... };
const doSomething = () => { //... };
const runFunctionThatThrowsError = () => {
  try {
    functionThatThrowsError();
  } catch (ex) {
    console.log(ex);
  }
}

const runEverything = () => {
  doSomething();
  runFunctionThatThrowsError();
}

The code above isolates the exception throwing and handling code into its own function, which makes it clear to the reader that the particular throws an exception that needs to be handled.

Don’s Repeat Yourself

Repeating code is definitely a big no-no. Repeated code has to be changed in multiple places when one thing changes. It’s also easy to miss repeated code.

The code also becomes a lot more bloated since they’re repeated in different places.

There’re many ways to eliminate duplicated code in JavaScript, like with functions and modules. We should use those as much as possible.

If we have repeated object declarations, then we should also use classes as a template to create those objects.

Repeated literals should be assigned to a constant and reused.

Conclusion

Output arguments should be eliminated because we don’t have to use them to change shared states in JavaScript.

Functions that do something should be separated from functions that return something.

Also, pieces of code that throw exceptions are preferred to code that returns error codes. The exception throwing code should be separated into its own function when we need to handle the exception to make our intentions clear that we want to handle the exceptions.

Repeating code is bad. It takes more time to change the code since we have to change them in multiple places. We should take steps to eliminate them by using code that can be accessed in different places.

Categories
JavaScript Best Practices

JavaScript Clean Code — Functions

Functions are important parts of JavaScript programs. They’re used for dividing code up into reusable chunks that do one thing.

Therefore in order to have clean JavaScript code, we have to have easy to understand functions.

In this article, we’ll look at how to write functions that are clean and easy to read and change. The most important thing is to write small functions.

Small Functions

Functions should be small. Smaller functions do less and it’s easier to read. Each line in a function should be around 100 characters long so they fit on any screen.

Doing less means less code means that it’s easier to read. If a function does more than a few things, then it should be divided into smaller functions.

Making small functions is very difficult in old JavaScript code since functions are used for many things they shouldn’t be used for like creating blocks and namespacing code.

However, now that we have JavaScript modules as a standard, we can gradually transform functions into doing things that functions are supposed to do, which is doing one thing.

For example, instead of creating blocks with functions as follows:

(function() {
  var numFruits = 1;
})();

We can instead write:

{
  let numFruits = 1;
};

They both create isolation, but instead of abusing the use of functions, we instead have just an isolated block of code. We can do this with ES6 or later.

let and const should be used to create block-level variables and constants respectively.

In addition, we can use modules if we want to put related pieces of code into a group. They can be used by importing the exported members of another module.

For example, we can create a file called fruit.js that exports a member as follows:

export color = 'red';

Then we can import it in another module called main.js as follows assuming that they’re in the same folder:

import { color } from './fruit'
console.log(color);

Then we get code isolation without using functions.

Blocks and Indent

Indentation should be automatically done with code formatters. We should have conditional statements and loops that are indented with 2 spaces.

Spaces are better than tabs because they don’t create issues with different operating systems. Tabs might look messed up in some systems.

We can use ESLine, JSLint or other linters to deal with indentation if we aren’t using a text editor that does formats JavaScript code automatically.

Do As Little As Possible

Usually, good functions should only do one thing. Long function is hard to read and if they have a lot going on, then it confuses the reader of the code.

The one thing may be hard to know. If it does more than one action, then it’s probably too much.

For example, code for rendering simple HTML to the user can be one function since that’s all it does.

However, if the HTML has many parts in it like looping through items retrieved from an API in multiple places and if statements, and so on, then they should split up into their own function.

If one function has lots of conditionals and loops, then they can probably be split into their own functions.

Another way to know if we can move something into its own function is that we can describe the piece of code without restating the implementation of the function.

One Level of Abstraction

Each function should only have one level of abstraction. This means if a function does something that has a high level of abstraction then it should only do that.

For example, if we want to write a function that loops through elements of an array and adds it to a list, then it should only do that.

Below is an example of dividing code into functions by the level of abstraction:

const addFruitLis = (fruits, ul) => {
  for (const f of fruits) {
    const li = document.createElement('li');
    li.innerHTML = f;
    ul.appendChild(li);
  };
}

const addFruitUl = (fruits) => {
  const ul = document.createElement('ul');
  addFruitLis(fruits, ul);
  document.body.appendChild(ul);
}

const fruits = ['apple', 'orange', 'grape'];
addFruitUl(fruits);

In the code above, we have a function addFruitLis that create the li elements and append it to the ul element that’s in the parameter.

This is one level of abstraction because we’re adding the li elements after the ul element is generated. It’s one level below the ul in terms of hierarchy.

Then we defined the addFruitUl function to create the ul element and delegate the addition of li elements to the addFruitLis function. Then the ul is appended to the document’s body. This way, each function only does as little as possible.

Finally, we call the addFruitUl function by passing in an array and then we get the elements on our page.

Each function only deals with one level of abstraction, as addFruitLis only deals with the li elements in the ul element and addFruitUl only deals with the ul element.

The wrong way to write the code above would be to combine everything into one function. It makes the function’s code complex and confusing.

Conclusion

Functions should do a little possible. We can do this by isolating them in blocks and modules. Old code that uses functions to do that should be eliminated.

Also, each function should do as little as possible and only deal with one level of abstraction. Otherwise, the function’s code gets long and confusing.

Categories
JavaScript Best Practices

JavaScript Clean Code — More About Naming

Writing clean, readable, and maintainable code takes some effort. One of the most basic parts of a program is identifiers. To make reading and changing the code easier, the naming of them has to be good.

In this article, we’ll look at some ways to name things in an easy to understand way.

Avoid Mental Mapping

Developers shouldn’t have to map their names into their meaning in their brains.

For example,

let r;

is a bad variable declaration for something that holds a URL string since there’s nothing about URL in the name r .

We can just name it url like we wanted to save the effort of mapping it in our brains.

Clarity is the most important thing when naming things.

Class Names

In JavaScript, class names are upper camel case. So FruitStand is a good name and fruitStand is not a good name for a class.

We should stick with convention to avoid confusion and mistakes.

Method Names

Method names should have camel case names in JavaScript. For example, getName is a good name for a function but GetName isn’t.

Don’t Use Clever Names

We should use clever names to confuse readers of our code. For example, holyCow is a bad variable for something that holds the number with the number of fruits since it doesn’t convey the actual content of the variable.

It makes the code hard to read and it’s not really funny.

Instead, we should name if numFruits so people actually know that the variable holds the number of fruit.

We should name things by what they mean.

Pick One Word Per Concept

We should pick one word per concept. For example, if we want to convey that a function gets a value. Then we shouldn’t have fetch, get, or grab in different function names.

Picking one word per concept reduces the mental capacity required for developers to understand the code since everything is predictable.

No Puns

Having 2 words for the same purpose in identifiers makes them confusing. Therefore, we should avoid them since they can be interpreted differently by different people.

For example, augment is a bad name for a function that can mean adding entries to arrays, or appending things to a string. So we should name it with the name that shows what it actually does.

Name that Developer Understand

It’s important to name things that developers understand. In our code, we should name things that are relevant to the domain of the solution.

For example, many developers know what a MessageQueue or Database is. Therefore, those are good names.

Problem Domain Names

If there’re no names in technical speak that developers understand, then we should name things in a way that describes the problems that we’re trying to solve.

Meaningful Context

We should name things with meaning context to our identifier names. For example, username and password are good names because we know that these variables are about the user entity which is used for logging into a system.

On the other hand, generic names like number and modifier aren’t good variables names because they don’t provide much context about what we’re set to values of these variables to.

If we’re using these names to construct a string that has the quantity of something and then the modifier for the thing we’re quantifying, then we should write something like:

let numberOfItems = 1;
let pluralModifier = numberOfItems ? '' : 's';
let numItemsString = `${numberOfItems} item${pluralModifier}`;

As we can see, these names are much more meaningful than number and modifier . They provide us with the context of the situation that the variables are used.

Don’t Add Useless Context

Useless context is also bad. For example, if we prefix every identifier with foo or Foo because we’re writing software for the company Foo, that’s bad because we already know that the software is for company Foo, so we don’t need to add the company name to everything and take up more space on the page and disk space.

We only need enough information in our names to convey the meaning of the identifier. If we can do it in a shorter way, then it’s better.

Naming things the right way takes some thought. We should add only enough context that people will gain a better understanding of the code.

Also, there shouldn’t be a need for developers to mentally map the names to something in their minds. If it’s needed then the name has to convey more meaning.

Clever names and puns are also bad since they’re misleading.

Finally, sticking to conventions of JavaScript is much needed so avoid confusion.