Stop Writing Code Comments

Stop Writing Code Comments

Stop writing code comments.

There is usually a high correlation between bad code and code with a lot of comments. This is the most obvious sign of messy source code.

The goal of every programmer should be to write code so clean and expressive that code comments are unnecessary. The purpose of every variable, function and class should be implicit in its name and structure. When you need to write a comment, it usually means that you have failed to write code that was expressive enough. You should feel a sharp pain in your stomach every time you write a comment.

When someone else reads your code, they should not have to read the comments to understand what your code is doing. Well named classes and functions should guide the reader through your code like a well-written novel. When the reader looks at a new class or function they should not be surprised by what they see inside. Remember, very little of a developer’s work time is actually spent writing code, much more time is spent on reading code and understanding what it does.


Comments Cover Up Failures

I often see comments above variable or function names describing what the code does (or is supposed to do). These comments make it clear that the programmer was not able to think of an expressive enough name or that their function is doing more than one thing.

Naming things in your code is extremely important. You should put a lot of effort into naming every piece of code accurately and precisely so that other developers can understand your code.

// find employees by status
List<Employee> find(Status status) {
  ...
}

In this example, the name find was not descriptive enough, so the author of this function needed to leave behind a descriptive comment describing what the function does. When we see the find function called from another module, it is a mystery what it does. What is it finding? What exactly does finding mean? Is it returning what it finds? How is it finding whatever it finds?

We don’t want to have to examine the comment above each function to understand what it does.

List<Employee> getEmployeesByStatus(Status status) {
  ...
}

Now it is obvious what this function does just by reading its signature, which makes the comment redundant. This brings me to the next way comments are failures.


Redundant Comments

These clutter up your code and are completely unnecessary. Adding many redundant comments trains the reader to skip over every comment, so when there is a comment that is important it will likely not be read.

// this function sends an email
void sendEmail() {
  ...
}

// this class holds data for an employee
public class Employee {
...
}

/**

  • @param title The title of the CD
  • @param author The author of the CD
  • @param tracks The number of tracks on the CD
    */
    public void addCd(String title, String author, int tracks) {
    ...
    }

The last example is mandated redundancy. Many organizations require this above every function and class. If your boss requires this, ask them not to.


Wrong Level of Abstraction

If you have a long function or need to document which part of your code does what, then you might be violating these rules:

  1. Functions should do one thing.
  2. Functions should be small.

Here is an example

// This function calculates prices, compares to sales
// promotions, checks if prices are valid, then
// send an email of promotion to user
public void doSomeThings() {

// Calculate prices
...
...
...

// Compare calculated prices with sales promotions
...
...
...

// Check if calculated prices are valid
...
...
...

// Send promotions to users
...
...
...
}

If you have pieces of code that can be separated into their own functions, then refactor it. When you have successfully encapsulated each portion of logic into a separate function, the code should read like a description of what it does.

Refactored we have this:

public void sendPromotionEmailToUsers() {
calculatePrices();
compareCalculatedPricesWithSalesPromotions();
checkIfCalculatedPricesAreValid();
sendPromotionEmail();
}

Instead of commenting each part of your code, each chunk of logic should be nicely encapsulated in its own function.

First, this improves readability. Each chunk of code does not have to be read line by line. We can instead simply read the helper function name and understand what it does. If we want to know further details inside each function, we can always see the implementation.

Secondly, it improves testability. In our example above, we can make a separate test for each function. Without encapsulating these separate functions it is difficult to test each part of the larger function sendPromotionEmailToUsers(). Functions that do more than one thing are hard to test.

Lastly, it improves refactorability. By encapsulating each part of the logic into its own function, future changes will be easier to do and will be isolated to changing the behavior of that function only. When we have long functions with local variables that persist throughout the entirety of the function, it is hard to refactor the function without causing changes somewhere else because of how tightly coupled the function is.


Commented Out Code

Commented out code should be treated like roadkill. Don’t look at it, don’t smell it, don’t ask where it came from, just get rid of it. The longer you keep it there, the longer it makes the rest of the code smell.

/*
public void oldFunction() {
noOneRemembersWhyIAmHere();
tryToUnCommentMe();
iWillProbablyCauseABuildFailure();
haHaHa();
}
*/

If you try to uncomment the code, who knows if it will even compile? Will that section of code invalidate something else in the code? Just delete it. If you need it later, you can always check your Version Control System, because you are using a VCS, right?


TODO Comments

Don’t write TODO comments, instead maybe just… do it? Most of the time these comments get forgotten about and later on might become irrelevant or wrong. When another coder sees the TODO comment later on, how do they know if this still needs to be done? Delete these comments too.

The only time a TODO comment is ok is if you are waiting for a merge from another teammate. This is not meant to last a long time, just until you can make the fix and submit it.

“When you feel the need to write a comment, first try to refactor the code so that any comment becomes superfluous.” — Martin Fowler
Comments Lie

Another problem with comments is that they lie to us.

When Jimmy makes a comment above the new function he wrote, he thinks he is helping out any future developer that sees his code. What he is really doing is setting a trap. His comment may lie (no pun intended) dormant for months or years not being touched, just waiting to become a nasty lie. Then one day during one of the hundreds of refactors and requirement changes, his comment becomes invalidated from some far away module.

When you change a line of code, how do you know you didn’t just invalidate a comment somewhere else in the code? There is no way to know. Comments must rot.

public class User {
...

// this holds the first and last name of the user
String name;

...
}

Then later on a developer wants to split name into firstName and lastName.

public class User {
...

// this holds the first and last name of the user
String firstName;
String lastName;

...
}

Bam! The comment is now wrong. You could update the comment to reflect the changes, but do you really want to manually maintain all of your comments after each change? You are a developer, not a documentor.

But this comment is easy to notice and is no problem to change.

Let’s see another example:

// Process employees based on status
void processEmployees() {
...
List<Employee> employees = findEmployees(statusList);
...
}

// this finds Employees by a list of statuses
List<Employee> findEmployees(List<String> statusList) {
...
}

Later on someone changes the function findEmployees so that it finds employees by a list of names instead of a list of statuses.

// Process employees based on status
void processEmployees() {
...
List<Employee> employees = findEmployees(statusList);
...
}

// this finds Employees by a list of statuses
List<Employee> findEmployees(List<String> nameList) {
...
}

First, the comment above findEmployees has been invalidated, so that needs to be changed. No problem, right? Wrong.

The comment above processEmployees has also been invalidated, so that needs to be changed too. How many other comments were just invalidated by this small refactoring? How many lies were created in the source code by this one change?

Alternative Solution:

void processEmployees() {
...
List<Employee> employees = findEmployeesByName(nameList);
...
}

List<Employee> findEmployeesByName(List<Name> nameList) {
...
}

If you name your functions accurately and precisely, comments will not be needed, and you won’t spread lies in your code.

“Code never lies, comments sometimes do.” — Ron Jeffries
When Comments Are Ok

I know many developers are diehard supporters of code comments, and to them I must admit that sometimes comments are ok. You should still feel squeamish every time you write one, but sometimes they are a necessary evil.


Complex Expressions

If you have a complicated SQL or regex statement, then go ahead and write a comment. It can be difficult to express statements such as these cleanly and expressively in your code. Writing a comment above these expressions will help your fellow developers out greatly in their understanding of your code.

// format matched kk:mm:ss EEE, MMM dd, yyy
Pattern timePattern = Pattern.compile("\d*:\d*:\d* \w*, \w*, \d*, \d*");

Warnings

If you need to warn other developers of side effects or bad things that will happen, it is ok to leave a comment near this code. These comments can act as harbingers of mysterious behavior in your code and add value to your code.


Clarification of Intent

If you can’t write expressive code, own up to your failure and write a comment. You are responsible for the code you write, so never leave poorly written code unexplained.

If you must write a comment, make sure it is local. A non-local comment placed far away from what it is referencing is destined to rot and become a lie. A comment referencing a function or variable should be directly above it. A warning comment can be above or beside the code it is referencing. If your IDE supports comment highlighting, make your warning comments stand out from the rest of the code.

I have established my feelings about code comments. I despise them, but I recognize that sometimes they are needed.

So please, stop writing so many comments.