bad-code-confusing_0.png

Lack of Comments Is a Bug

As a young developer, I attended a conference session about "code smells." It was presented by an awesome senior developer who actually taught classes at a local college. He had some strong opinions about code, and almost everything he said was met with nodding heads. His suggestion that a lack of commenting or even poor commenting was a "code smell" was met with some resistance from the audience. I thought it was inspirational. Many years later, I'm ready actually to take his suggestion and raise the bar. Poor commenting hygiene is a BUG.

How can it be a bug?

I can already hear people grumbling out there... how can a comment (or lack of a moment) be a bug? It doesn't affect the functioning of the code or break the expected results. That may be true, but I suggest that this view of your code is too narrow in time. Code is rarely frozen and set in stone. It tends to evolve and change over time with new requirements, new bugs, refactoring, etc.

In the realm of software development, the act of writing code is often compared to art. Like any good art, it's not just about the final product but also about the process and the story it tells. This is where commenting plays a crucial role. Comments are the narrative that guides others through the code's logic, decisions, and structure. They are the annotations in the margins of a book, the footnotes in an academic paper, and the guideposts on a trail.

Always write your code as if the next person who will work on it is an axe-wielding maniac... who knows where you live... and will probably be you.

The comments you write today aren't necessarily for you today. They are for you tomorrow... or the next developer. They are a way to make it clear what is going on and why certain choices are made. Good comments don't just explain what's going on but are actually part of best practices that help prevent bugs and mistakes from creeping in.

The Importance of Commenting in Code

When you think about your code's entire lifespan and lifecycle, comments become critical. We've had them in code pretty much from the beginning. The first programs were punchcards and paper slips... but the developer wrote the "code" by hand on a form, which usually had room for comments. 

Comments were maybe written in pencil on the tape, with the room being made for them by omitting the clock marks. If you wanted a comment, you’d “patch” a section of blank tape into the paper tape, and get out your pencil.

-- Terry Lambert

There are plenty of good reasons to comment on your code, but some of the most important are:

  1. Describing how to use it
    This is the most obvious use of comments - to describe how the code works and how to use or extend it. Embedding comments *inside* the code is like putting lines and arrows on the streets. It puts clear instructions where you need them most. This is particularly important when the code may not be self-explanatory or complex logic requires justification.
     
  2. Explaining why certain decisions were made
    Good code isn't perfect. We often have to use workarounds or less-than-ideal solutions due to constraints on time, resources, ability, etc. A quick note explaining why a choice was made can be very helpful in understanding why this code was created this way and give clues on how to refactor or improve it later.
     
  3. Async communication
    Comments serve as a communication tool between developers. In a team setting, where multiple people work on the same codebase, comments help team members understand each other's work without the need for constant verbal or written communication. This is also why you will often see funny or "colorful" comments in code bases.
     
  4. Built-in rubber ducking
    The act of writing comments acts as a way to review your code and flow. Often, this leads to a different understanding, finding a problem, discovering some missing needs, or simply a better way to refactor your code. This is probably the most underappreciated benefit to good commenting.
     
  5. Auto documentation
    With the right format, you can use tools like JSDoc to automatically generate documentation based on your comments. It's a pretty cool outcome to good coding - stand-alone documentation that tells people everything they need to know about using your code.

Protip: When working on a class or a group of functions, write out the comment blocks first - this will help you figure out the flow and quickly "wireframe" your code. As a bonus, once you have done this, you can use your comments to get an AI assistant to write a rough draft for you to start with.

Above all, comments are a lifesaver for future maintenance. Code without comments can become a tangled web that is difficult to decipher, even by the original author, after some time has passed. Comments act as a roadmap, making it easier to navigate and modify the code later on. This is why a lack of comments, or poor comments are a bug - they are introducing potential problems that will almost certainly blow up in the future.

Comment types and when to use them

There are two primary types of comments: inline and block. 

Inline comments are short explanations or annotations within the lines of code, typically used to clarify a specific line or block of code. They are like whispers in the ear, providing quick, just-in-time explanations. Generally, it is best to use inline comments sparingly - your code and naming convention should make things readable. However, using inline comments for code sections to give an overview within a function is a good idea.

// Calculating the area
let length = getLength();
let width = getWidth();
let area = length * width; 

Block comments, on the other hand, are more like paragraphs, providing a broader explanation or overview. They are used to describe more complex logic or give an overview of what a section of code or a function does. Typically, these are used before functions, methods, global constants, and classes are declared. Ideally, they also use doc block style formatting. 

/**
 * Calculates the area of a rectangle.
 * @param {number} length - The length of the rectangle.
 * @param {number} width - The width of the rectangle.
 * @returns {number} The area of the rectangle.
 */
function calculateArea(length, width) {
  return length * width;
}

Both types of comments have their place. Inline comments are best for brief, line-specific notes, while block comments are ideal for documenting functions, methods, and classes.

Standard Commenting Formats Like JSDoc

Standard commenting formats, such as JSDoc, bring structure and uniformity to the commenting process. JSDoc, in particular, is a markup language used to annotate JavaScript code. It provides a consistent way for developers to add information about the functions, parameters, return values, and more.

The importance of using a standard like JSDoc cannot be overstated. It allows for the generation of documentation automatically, saving time and ensuring that the documentation is always up to date with the code. It also facilitates tooling support, such as code completion and type checking in modern code editors, enhancing developer productivity.

Moreover, a standardized format makes it easier for new developers to read and understand the codebase, as they can quickly learn the commenting conventions and find the information they need. This is reason enough to use comments. New developers in your project will be able to figure things out faster and with less outside help. In fact, if your comments are good enough, then no one should need help. The help is embedded in the code.

A Guide to Using Tags (with Code Samples)

If you look at the JSDoc docs (ha!), you will see that there are a large number of tags available for use. However, in most cases, you will be using just a handful of tags over and over. If you get familiar with these few tags and use them in your code, you will get most of the benefits without much work. 

6 key tags to use

These six tags are the ones you will use the most. I try to include these in all of my code as a default. Its a good habit.

@module

This tag provides information about the module or file, often including a high-level description.

Format: text description

/**
 * @module mathHelpers
 * This module provides utility functions for mathematical operations.
 */

@class

This tag provides information about the class.

Format: text description

/**
 * @class mathHelpers
 * This class provides utility functions for mathematical operations.
 */

@function

This tag describes the function itself. Often people may omit this tag and just write the description in the comment block. However, it can also be used in your code to note any constants that are set to functions.

Format: name and optional description
Synonyms: @func, @method

/** @function **/
 const checkMath = add(num1, num2) => {};

@param

This tag is used to describe a parameter (or arguments) of a function. Parameters define the expected inputs of a function.

Format:  type (string, int, array, obj, etc), name, and parameter description.
Synonyms: @arg, @argument

/**
 * Adds two numbers together.
 * @param {number} num1 The first number.
 * @param {number} num2 The second number.
 * @returns {number} The sum of num1 and num2.
 */
function add(num1, num2) {
  return num1 + num2;
}

@returns

The @returns tag is used to describe the return value of a function. Return describes the expected output(s) of a function.

Format:  type (string, int, array, obj, etc) and description.
Synonyms: @return

/**
 * Retrieves the name of a user.
 * @param {number} userId The ID of the user.
 * @returns {string} The name of the user.
 */
function getUserName(userId) {
  // ...implementation...
}

@todo

This tag is a reminder of future work to do, especially when you are short on time and need to get something finished. Leaving a @todo will let you place a reminder for additional work so you can keep moving. Then, you (or a teammate) will see this tag next time they work on the code. Alternatively, you can search your code for @todo items and create tickets later.

Format: text description

// @todo figure out why this works

// @todo this needs to be optimized

/** 
 * @todo this is a temporary fix until ticket #28902 is resolved
 * @see https://github.com/appsmithorg/appsmith/issues/28902
 */

5 optional tags to consider

You may or may not use these tags, but are good to know about.

@example

The @example tag is used to provide a code example showing how to use a function or a module. This is usually not necessary if your comments are good, but if you are creating documentation or have a very unique implementation, then it can be helpful.

Format: text code example

/**
 * Calculates the area of a rectangle.
 * @param {number} length The length of the rectangle.
 * @param {number} width The width of the rectangle.
 * @returns {number} The area of the rectangle.
 * @example
 * // returns 50
 * calculateArea(10, 5);
 */
function calculateArea(length, width) {
  return length * width;
}

@author

The @author tag shows who created the code. If an email address is added, JSDoc will convert it into a mailto link when creating documentation. This is not just about bragging rights but also ensuring people know who to talk to when making changes. You can use email, social media handle, Github id, etc. to help users find you.

Format: Inline name and optional email (in brackets)

/**
 * Calculates the area by reading your mind to get the values
 * @author Ron Northcutt <notmy@realaddress.com>
 * @returns {number} The area of the rectangle.
 */
function telepathicArea(userId) {
  // ...magic...
}

@file

The @file tag provides a description for the file, and is at the beginning. Sometimes this can be a handy way to add some additional context that isn't available otherwise.

Format: Inline name and optional description

/**
 * @file mathHelpers.js - existing libraries for these functions are more extensive
 *     and larger than we need. 
 */

@requires

The @requires tag documents a module or library that is required to use the code. This is typically not necessary because it is handled in the file or implementation, but it can be a great way to identify where and how certain libraries are needed, especially when you look to refactor later.

Format: Inline name

/**
 * @requires module:appsmith/helper
 */

@see

The @see tag refers to another function, resource, or link to outside source.

Format: Inline text

/**
 * @see {telepathicArea} because that stuff is awesome
 * @see http://www.duckduckgo.com to search for how it works
 */

Single tags to describe function attributes

These tags don't have any text or additional information - they are simply placed in the comment block to tag them.

  • @async - this function is asynchronous
  • @deprecated - this function is deprecated

Bad comments will lead to failure

I started this post with a pretty strong stance - that poor comment hygiene (bad or no comments) is a bug. It is not a bug in the sense that it will cause your code to fail execution... but it is a bug in the sense that it will eventually cause your code to fail.

Commenting is not just a best practice; it's a vital part of writing maintainable, understandable, and collaborative code. By using standard formats like JSDoc and understanding the different types of comments, developers can greatly enhance the readability and usability of their code, making it not just a functional piece of art, but a well-documented one too.

Do you agree? Leave a comment below to let me know what you think.