Code Comments Are Not Evil

Grant Riordan - Jul 24 - - Dev Community

Code should be self-documenting, there’s no need for comments.

As a developer, you’ll have heard someone in your team say this phrase at one point if not many. It’s a common ideology many repeat, often because they think it’s the right viewpoint to have as a developer and not necessarily what they believe in themselves.

What’s My Opinion?

I feel that comments have a time and a place, especially with different programming languages. Don't overuse comments. I utilise comments when the code is not self-explanatory. Some systems I've worked on in the past can be convoluted or have some "weird" code in there for very specific situations.

I agree that function/method names should be self-explanatory, and developers should be able to read the code and know roughly what is done just by skimming through.

Complex Algorithms

When writing extension methods/functions in code that can use algorithms that others in the team may not know, or truly understand, comments can be extremely useful in explaining the inner workings.

The “self-documenters” would argue the code should be written in a way that would explain its intent without the comments, however, I strongly believe the comments explain the unusual algorithm to any current or future developer.

It can also explain the logic behind it. In the example below I’ve used comments to explain the all-important if statement to prevent a never-ending loop during recursion. We can also use it to explain the spreading / recursive syntax within the return statement, something without comments may not be obvious to some developers.

const quicksort = (arr: number[]): number[] => {

/* Base case: arrays with 0 or 1 element are already sorted, 
  return to avoid never ending loop */
  if (arr.length <= 1) {
    return arr; 
  }

  const pivot = arr[Math.floor(arr.length / 2)]; // Choose the middle element as the pivot
  const left = arr.filter(x => x < pivot); // Elements less than the pivot
  const middle = arr.filter(x => x === pivot); // Elements equal to the pivot
  const right = arr.filter(x => x > pivot); // Elements greater than the pivot

  // Recursively sort the left and right sub-arrays, then concatenate the results
  return [...quicksort(left), ...middle, ...quicksort(right)];
};
Enter fullscreen mode Exit fullscreen mode

Non-Obvious Workarounds or Hacks

Code often includes workarounds for bugs in third-party libraries or other non-obvious solutions. It's helpful to add comments to explain the changes / temp fixes.

Comments also enhance Intellisense with additional information, using formats like JSDoc (JS), TSDoc (TS), or comment XML notation (C#). The example below albeit verbose is merely for illustration purposes.

Example:


/**
Parses a date string in the format 'YYYY-MM-DD' to a Date object.
 This workaround ensures that the date is correctly parsed regardless of the user's time zone. Were seeing issues with JS Date usage across multiple timezones.
@param dateString - The date string in 'YYYY-MM-DD' format.
 * @returns A Date object representing the given date at 00:00 UTC.
 */
const parseToUtcDate = (dateString: string): Date => {
  const [year, month, day] = dateString.split('-').map(Number);

  // Workaround to ensure the date is parsed correctly in UTC
  const date = new Date(Date.UTC(year, month - 1, day));

  // Validate the parsed date
  if (isNaN(date.getTime())) {
    throw new Error('Invalid date format. Expected format: YYYY-MM-DD.');
  }

  return date;
};

Enter fullscreen mode Exit fullscreen mode

Clarify Intent or Business Logic

const applyDiscount = (user: { isPremiumMember: boolean; discount: number }): void => {
    if (user.isPremiumMember) {
        user.discount = 0.2; // Premium members get a 20% discount
    } else {
        user.discount = 0.0; // Non-premium members get no discount
    }
}
Enter fullscreen mode Exit fullscreen mode

Sometimes we have to implement temporary fixes to solve a problem, however to people unbeknownst of the problem, without comments they might be wondering why we’ve made a business logic decision, and the use of comments adds clarity.

const calculateTotal = (cart: Item[]): number => {
    //Temp Hotifx: Use 0 as the default price for items with undefined prices
    return cart.reduce((total, item) => total + (item.price ?? 0), 0);
}
Enter fullscreen mode Exit fullscreen mode

Bad Examples of Using Comments:

Redundant Comments: Adding comments that just aren’t needed. This is a perfectly good example of self-documenting code, there is no need for comments.

const addNumbers = (a: number, b: number): number => {
    // Add a to b
    return a + b;
}
Enter fullscreen mode Exit fullscreen mode

Misleading Comments: Adding comments which give misleading information

const multiplyNumbers = (a:number, b:number)=>{
  //Add the the two numbers
  return a * b;
}

Enter fullscreen mode Exit fullscreen mode

Overly Verbose Comments: There is such a thing as writing too much in your comments. You should aim to keep your comments direct, succinct and as small as possible.

/**
 * This function takes two arguments, a and b, both of which are numbers. It then
 * multiplies these two numbers using the multiplication operator (*) and returns
 * the result of the multiplication, which is also a number. Multiplication is a
 * basic arithmetic operation that combines two numbers into a single product.
 * For example, if a is 2 and b is 3, the result will be 6.
 */
function multiply(a: number, b: number): number {
    return a * b;
}
Enter fullscreen mode Exit fullscreen mode

Useless Comments:

Useless comments provide no meaningful information, sometimes stating the obvious or including trivial notes.

let count = 0; // Set count to 0

function increment() {
    count++; // Increment count by 1
}
Enter fullscreen mode Exit fullscreen mode

Commented-Out Code Without Explanation

Leaving commented-out code without any explanation as to why it's there can confuse future developers.

const calculateArea = (radius: number): number => {
    // const pi = 3.14;
    const pi = Math.PI;
    return pi * radius * radius;
}

Enter fullscreen mode Exit fullscreen mode

Unmaintained Comments

Unmaintained comments that no longer reflect the current state of the code can be misleading.

// This function fetches user data from the server
const getUserData = () => {
    // Fetch data from local storage
    const data = localStorage.getItem('userData');
    return data ? JSON.parse(data) : null;
}

Enter fullscreen mode Exit fullscreen mode

Personal Notes, Opinions & Bad Language

Including personal notes or opinions in comments is unprofessional and unhelpful.

// I tried so many f***g times to get this to work, this was the
// best I could do. 
const computeFactorial = (n: number): number => {
    // This is stupid, but it works...
    if (n <= 1) return 1;
    return n * computeFactorial(n - 1);
}
Enter fullscreen mode Exit fullscreen mode

Bad commenting practices can make code harder to understand, maintain, and secure. Comments should be meaningful, concise, and accurately reflect the code's purpose and behaviour. They should add value by explaining the "why" behind the code, rather than restating the "what" that is already clear from the code itself.

Conclusion

I am a strong advocate for the strategic use of comments in modern code. Comments play a critical role in providing context and clarifying complex logic that might not be immediately obvious. Without any comments, the next developer might struggle to understand decisions such as why a database is queried multiple times, potentially leading to confusion or even unnecessary code changes.

Here are some best practices for commenting in code:

  1. Use Descriptive Naming: Choose well-named variables, methods, and classes to minimize the need for comments. Clear names often reduce the need for additional explanations.
  2. Write Clear Code: Ensure your code is logical and well-structured. Good code design should make it easy for developers to understand the purpose and flow based on variable names and return types.
  3. Provide Purposeful Comments: Use brief comments to explain the purpose and rationale behind complex or non-obvious code. Focus on the "why" rather than the "what."
  4. Avoid Over-Commenting: Do not comment on everything just for the sake of commenting. Focus on areas where comments add real value.
  5. Skip Humor: Avoid using humorous comments in your code. While they might be entertaining, they are unprofessional and take up valuable space.

Not every line of code, function, or class needs a comment. Use your judgment to determine where comments are most beneficial for you and your team. If a comment helps clarify your thought process or the code's intent, others will likely benefit from it as well.

While striving for self-documenting code is a good practice, comments are essential for providing context, explaining complex logic, and documenting workarounds. They bridge the gap between the code's functionality and its purpose, facilitating easier understanding, maintenance, and extension of the codebase.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .