DEV Community

Cover image for Code Comments Are Not Evil
Grant Riordan
Grant Riordan

Posted on

Code Comments Are Not Evil

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.

Top comments (18)

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️ • Edited
user.discount = 0.2; // Premium members get a 20% discount
Enter fullscreen mode Exit fullscreen mode
// Add a to b
return a + b;
Enter fullscreen mode Exit fullscreen mode

I honestly don't see any difference between these two. They both simply spell out the code in prose without adding any additional information.

The only difference I see is that the first bit of code seems like the user discount should be extracted into a configuration source rather than hard-coded like that.

Meanwhile, an example like this:

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

Is ironically a situation where I would use a comment, but the example once again just spells out the code.

I read the code and notice 0 is used as the fallback price. I wonder how that could make sense and read the comment for more context only to find... what I already knew.

A better comment would have been:

// hotfix: import error caused price on free items to be NULL;
// this doesn't otherwise happen so these prices can always be
// treated as 0
Enter fullscreen mode Exit fullscreen mode

Now I read the code and know that it makes sense and isn't an error, and that somebody (maybe even past me) has already confirmed those things. It also gives me a lot more context regarding whether it might be time to remove this hotfix or it is still necessary: Since it was a one-off import error, I can check the database for NULL prices and if they've been fixed, remove the code.

And lastly:

Avoid using humorous comments in your code. While they might be entertaining, they are unprofessional and take up valuable space.

Nah, a bit of humour isn't unprofessional. Just avoid jokes that you wouldn't be comfortable telling to your coworkers or a stranger directly, and don't overdo it.

Collapse
 
grantdotdev profile image
Grant Riordan

Take your comments on board with the hotfix, however it’s a lengthy comment , the one used merely as an example purposes was to highlight it can be done and has a brief explanation.

Commit message could have more detail.

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

I'm not generally a big fan of putting information that relates to the code in a commit message. In theory you could always git blame to find out why something was written, but in theory code can exist outside of its repository, so using a comment puts the explanation closest to where it's needed.

A commit message should generally focus on the what was changed more than the how does the code work, so there I'd care more about seeing 1. that it's a hotfix 2. what it does to fix it and 3. some context as to why this was necessary.

Coming up with a good concise example of a comment for a dev post while still showing off the subtleties of writing good comments is definitely hard, and my 3 lines are already about the shortest example I could come up with (out in the real world, these things often get a lot longer).

But I think your examples did just get a little too simple here and there and you lost some of the points you were making in the text (which I mostly agree with, by the way).

Thread Thread
 
grantdotdev profile image
Grant Riordan

Thanks for the feedback.

Collapse
 
miketalbot profile image
Mike Talbot ⭐

Totally agree with this.

Collapse
 
axorax profile image
Axorax

Agreed. I also like the Hotifx in the 3rd code block

Collapse
 
ingosteinke profile image
Ingo Steinke, web developer

Another valid use case: TODO-comments

// TODO remove this workaround when framework bug #12345 got fixed
Enter fullscreen mode Exit fullscreen mode
Collapse
 
grantdotdev profile image
Grant Riordan

definately- check out my blog on how to ensure these actually get done dev.to/grantdotdev/tech-debt-code-...

Collapse
 
andrewmat profile image
André Matulionis

I agree with you that comments are stigmatized and we should be using more of this tool.

But where I disagree:

  • Complex code can use comments, but you should revaluate if this complexity is worth it, and if you could externalize its maintainancy. It may be better to add a sort package into your dependencies than create your own sort.

  • Overly verbose comments can be crucial. It should be used sparingly, but don't detract from your comment when trying to be succinct. It's better to have a paragraph than to not understand it.

  • A major argument I see against comments of that they are unmaintained by default. Comments should be updated, but nothing is making you update it. Just saying "don't let your comments be unmaintained" kinda skips this point placing the burden into the dev, and I miss this being addressed in your text.

I agree with most of your arguments, but had to say this

Collapse
 
wild-leo profile image
Leo

I agree with you. However I think the way we want to convince ourselves, what is right and what is wrong, is somehow corrupted. Since you're trying to make sense out of it, and give this some reasonable explanation you've just entered battle you can't really win.

For example somebody pointed out this code:

user.discount = 0.2; // Premium members get a 20% discount
Enter fullscreen mode Exit fullscreen mode

is just simply redundant information... and it IS!
But I clearly see your point, and would advocate for this case.

We're trying to use some sense and logic in this debate... but comments are essentially way of communicating from one developer to another. And there are no mathematical rules in the way we speak. We usually rely on our guts.

And that's my point, we should just follow our intuition, still debate, but approach it more like a telling a story.

For me we should loosely follow those rules:
- use comments, but don't overuse them
- avoid stating obvious
- keep in mind comments need to be up to date
- comments are great for pointing out important context of code
- don't trust comments, there are only hints

And lastly it's not really that important. If you can give someone 15min advice how to improve commenting skills, go ahead. But lately I've met people who spend absurdly large amount of time correcting others how they should comment... and that's just counter-productive.

Collapse
 
tnypxl profile image
tnypxl

Comments are great when you're working in legacy code. In situations where you have full control of the code from top to bottom, write code that is clear, not clever.

Collapse
 
martinbaun profile image
Martin Baun

I like this take, and your piece is very well written!

Collapse
 
best_codes profile image
Best Codes

This is a great article, I love it! Thanks for writing.

Collapse
 
axorax profile image
Axorax

Code comments may not be evil but the developer writing the comments may be! 😶‍🌫️

Collapse
 
matthewbdaly profile image
Matthew Daly

One case you don't mention is type hints. If you can document the type of something with native type hints, that's always a better option, and documenting it with comments as well is redundant unless there's additional information that can be expressed that way that can't be expressed with native type hints.

Collapse
 
grantdotdev profile image
Grant Riordan

As type hints are tightly coupled with Python language, and this article is predominantly aimed at a more generic level, I feel it would be misplaced and irrelevant.

Collapse
 
matthewbdaly profile image
Matthew Daly • Edited

Actually I wasn't referring to Python in this context. I work mostly in PHP and that's what I had in mind in my response.

Tools like Psalm let me express a lot more information about types than the native types can. For instance, I can specify that a string is the name of a class or interface, which can't be done with native type hints in PHP.

Collapse
 
rubdottocom profile image
rubdottocom

🤡

// TODO: Add more comments
Enter fullscreen mode Exit fullscreen mode