by Francesco Agnoletto

About code comments

// Magic. Do not touch.

Comments are always a controversial topic among developers. Some love them and comment every line of code, while others avoid them at all costs.

Who needs comments and why

Why comments are useful is a question as old as the first programming language.

You are the first person who will need your comments. If you worked on it, there are high chances you are going to touch it again in the future. You will not remember the reason why you did things a certain way. A well-placed comment can save you a few hours of debugging, even more, if it’s another person code.

Temporary comments

Comments are not all born equal, some are not meant to last forever while others have more lasting lives.

Temporary comments include requests for refactoring or enhancements left out during development. Usual reasons include lack of time and waiting for unreleased library features.

// TODO add React.memo when react@16.6 gets released
const Dropdown = (props) => (
  <div>
    ...
  </div>
)

All temporary comments must be actionable. Either immediately (given the time) or after a set event happens, possibly not years in the future. To make these comments easy to search and identify, it is a good practice to start them with the same formula.

I use // TODO, easy to search when you can dedicate time to remove a few of them.

try {
  const response = await fetch('my-api');
  return await response.json();
} catch (error) {
  // TODO handle error
}

Permanent comments

These are a different type of comment, their purpose is to explain your choices or more complex code. Do not overdo it. An obvious comment is a useless noise.

Explanation of code

Comments should explain code that takes more than 30 seconds to read.
Language features and API specific code rarely need explaining, but edge cases do.
If the code uses native language features in an unusual way, it requires a comment.
If the code uses library specific code in an undocumented way, it requires a comment.

try {
  const response = await fetch('my-api');
  return await response.json();
  /* this executes if there's an error in the fetch above */
} catch (error) {
  console.error(error);
}

The above comment displays an unneeded comment. The try/catch block is a standard feature of JavaScript introduced in 1999. This case is a very standard one, it doesn’t need a comment. The MDN article on it uses a similar example.

/*
  create an array with all inputted strings
  split all strings in array
  remove all duplicates and turn it to string
  input: "bob" "alice" "john" "bob bob dan"
  output: "bob alice john dan"
*/
const deduplicateStrings = (...string) => [
    ...new Set(
      [...string].join(' ').split(' ')
    )
  ].join(' ');

This example is hard to read. It uses Array destructuring and Set both ES2015 features, in an unconventional way.
A comment explaining its functionality improves its readability.

While a test could achieve the same result it is not a substitute. Tests make sure the code works as intended, they shouldn’t explain the code.

Explanation of intent

Sometimes the intent is different than what would seem easier to do. Be it a limitation of the code/framework/project or a specifically requested feature.

/*
  'my-api/a&b&c' is super slow
  to make the request smaller we only request A & B and calculate C
  see github.com/kornil/this-project/issues/21
*/
const FetchABC = async () => {
  const response = await fetch('my-api/a&b');
  const data = await reposnse.json();

  return [a, b, a + b];
}

In this case, developers opted for an alternative instead of the default way. The comment documents why and where the decision took place.

/*
  React-table formats our data before giving us back cellProps.
  Problem is that booleans become undefined
  so we have to apply toString() before the data is given to react-table.
*/
const cleanData = rawData.map((data) => {
  return data.hasOwnProperty('live') ?
    { ...data, live: data.live.toString() } : data;
});

The undocumented behavior of a dependency is a standard case for an explanation comment.

Tips for good comments

Using different styles of comments can make it easier to distinguish them. JavaScript allows two different types of comments, // and /* */. Using the first for temporary comments can help recognize actionable ones.

If a colleague asks the reason behind a choice, the code deserves a comment.

If there is commented out code, remove it. It will still be available in the git history. The codebase doesn’t need the noise.

If it is easy to read, it probably doesn’t need a comment.
If it’s too complicated, it probably needs a refactor.

A good variable name can add more value than a comment.

Working alone is not an excuse to not write comments, and neither is “we will remember it”.