Drew‘s Tech Blog

Why You Should NOT Comment Your Code

By Drew Thomas | Published 3/27/2024

As a software engineer at Amazon with a history of working with Startups, consulting for small businesses, and leading teams in college, I‘ve had the opportunity to work on a wide variety of codebases over the years, from legacy spaghetti to pristine works of art. One topic that often sparks debate among developers is code commenting... a heated happy hours discussion inspired me to write this post. While comments can serve a purpose, I‘ve come to believe that in most cases, you should strive to write code that is so clear and well-structured that it doesn‘t require explanatory comments. In this post, I‘ll share my perspective on why you should generally avoid commenting your code.

Well-Constructed Code Speaks for Itself

The primary argument against relying heavily on code comments is that if your code is thoughtfully designed and implemented, it should be readable and understandable without the need for supplementary explanations. Clean, modular code with descriptive names and a clear flow tells a story on its own. Consider this example:

 
    // Check if the user is eligible for a discount 
    if ((user.age >= 65 && user.hasMembership) || (user.age < 18) || (user.hasMembership && user.loyaltyPoints > 100)) { 
      // Apply a 20% discount 
      totalPrice *= 0.8; 
    } else { 
      // Apply a 5% discount if the total price is over $100 
      if (totalPrice > 100) { 
        totalPrice *= 0.95; 
      } 
    }
    

Now, compare it to this:

 
    const SENIOR_AGE = 65; 
    const MINOR_AGE = 18; 
    const LOYALTY_POINTS_THRESHOLD = 100; 
    const MEMBER_DISCOUNT = 0.8; 
    const LARGE_ORDER_DISCOUNT = 0.95; 
    const LARGE_ORDER_THRESHOLD = 100; 
    
    function getDiscountedPrice(user, totalPrice) { 
      if (isEligibleForMemberDiscount(user)) { 
        return totalPrice * MEMBER_DISCOUNT; 
      } else if (isEligibleForLargeOrderDiscount(totalPrice)) { 
        return totalPrice * LARGE_ORDER_DISCOUNT; 
      } 
      return totalPrice; 
    } 
    
    function isEligibleForMemberDiscount(user) { 
      return isSeniorWithMembership(user) || isMinor(user) || isLoyalMember(user); 
    } 
    
    function isEligibleForLargeOrderDiscount(totalPrice) { 
      return totalPrice > LARGE_ORDER_THRESHOLD; 
    } 
    
    function isSeniorWithMembership(user) { 
      return user.age >= SENIOR_AGE && user.hasMembership; 
    } 
    
    function isMinorWithoutMembership(user) { 
      return user.age < MINOR_AGE; 
    } 
    
    function isLoyalMember(user) { 
      return user.hasMembership && user.loyaltyPoints > LOYALTY_POINTS_THRESHOLD; 
    } 
    

In the second example, the code is self-explanatory. The function and variable names clearly convey their purpose, making comments redundant.

The Perils of Stale Comments

Another risk of relying on comments is that they can easily become outdated as the codebase evolves. When a comment doesn‘t keep pace with the actual code, it becomes a source of confusion rather than clarity. Inaccurate comments can mislead developers and introduce bugs.

I can‘t count the number of times I‘ve seen comments like this:


    // This does X
    function doX() {
      ...
    }
    
    // This does Y
    function doY() {
      ...
    }
    
    // This does Y
    function doZ {
      ...
    }
    

Over time, similar functions were added by copying and pasting from the function above them. The comments came with them, but didn‘t get updated. When I‘m reading this code, I have to wonder if doZ is supposed to do what the comment says, or if the comment is wrong. Would somebody seriously take the time to lie to me? It‘s a good thing they wrote easy to read unit tests so I can find out ;).

Code is Precise, English is Ambiguous

Code by its very nature is unambiguous. It specifies precise instructions for the computer to follow. In contrast, natural language is often imprecise and open to interpretation. Two developers can read the same comment and come away with different understandings. If intentions could be clearly communicated, I wouldn‘t have a job, and you probably wouldn‘t be reading this post. LLMs such as Claude Opus (which I used to build this blog) would render engineers obsolete.

The Judicious Use of Comments

Now, this isn‘t to say comments have no place in code. Like most things in programming, the answer is "it depends." There are situations where comments are quite valuable:

  • Explaining why code is written a certain way when it‘s not obvious
  • Clarifying complex algorithms or domain-specific logic
  • Highlighting important todos or areas for improvement
  • Documenting public APIs

In his seminal book "Clean Code", which shaped most of my opinions, Robert C. Martin offers this guidance:

"Every time you write a comment, you should grimace and feel the failure of your ability of expression."

When I leave a comment, I‘m apologizing to the poor soul that comes after me. "I was not good enough to make this comprehensible. Here‘s my excuse, I hope you fair better than me." I‘m not scared to apologize and when it‘s necessary and well placed, it can make a world of difference... the reader might just forgive me.

Conclusion

As you navigate your programming journey, challenge yourself to write code that is expressive and self-explanatory. Use clear names, keep functions focused, and maintain a logical flow. Treat comments as a last resort, not a first instinct. Remember, the best comment is often a better line of code.

Note: If you found this to be an interesting read and want to know more about when comments are useful, and when you should save the bytes, I highly recommend reading Clean Code by Bob Martin. Chapter 4 is dedicated entirely to comments, and the rest of the book transformed how I think about writing code. It‘s played a large factor in my success at Amazon, and I often reference its ideas when leaving feedback on code reviews/pull requests. If you wanna support more posts like this, here‘s an affiliate link to Clean Code on Amazon.

Happy coding!