A Subtle Art Of Writing Good Code Comments

igorfil

Igor Fil

Posted on December 28, 2020

A Subtle Art Of Writing Good Code Comments

Today I want to explore somewhat overlooked topic of code commentaries. It seems like a simple topic, yet it is the one that sometimes invokes the hottest water cooler arguments! But why do we debate about it, why do we understand it differently? What is a bad code comment and makes a good code commentary? Why do we write code comments in the first place?

Some argue that code must be self-explanatory and there must be no code comments at all, that code comments are a sign of badly written code. On the opposite side of the spectrum is “comment every line” camp, those who insist on extensive comments of every piece of code. But where is the right balance?

Over the years everyone has seen lots of code comments. Some of them were really useful, others were useless, while some were utterly harmful. Looking at those, everyone has developed his vision of what code comments should look like.

Let’s take a look at some examples.

Useless comments

Sometimes code comments doen’t bring any value. They don’t seem to add any additional information and seem to be only exploding line count.

For example:

  • A comment that states the obvious
/* Sets price */
public void setPrice(int price) {...}
Enter fullscreen mode Exit fullscreen mode

Such comment does not bring any additional value. It just re-states something that is already obvious from a function name.

  • Comments on every line
// validate request
validateRequest(request);

//extract data from request
SomeData data = request.getData();

//Process data
dataProcessor.process(data);
Enter fullscreen mode Exit fullscreen mode

Such style of commenting is a variation of the previous one. It usually starts with good intent to explain every step of the process. But in this case using good naming convention and Single Responsibility Principle gets you all the way there. Splitting big chunk of code into smaller functions with clear name and sole purpose gives not only provides you with such step-by-step explanation, but makes comments useless.

  • Arrange-Act-Assert comments in unit tests
public void testGiveNullReturnsEmpty(){
    //arrange
    Converter converter = createDefaultConverter();

    //act
    Optional<SomeComversionResult> result = converter.convert(null);

    //assert
    assertTrie(result.iEmpty());
}   
Enter fullscreen mode Exit fullscreen mode

Following Arrange-Act-Assert structure of unit tests is a great idea. But conventionally this separation in code is achieved by using a new line between sections. This is a common practice and anyone familiar with this pattern will recognize it immegiately. Stating them explicitly does not add any clarify, just adds clutter.

Harmful comments

Sometimes code comments can bring more harm then good. They can confuse or mislead.

  • Clarification of what variables mean
// time delta
private long t_d;
Enter fullscreen mode Exit fullscreen mode

Typing "t_d" might take less keystrokes, yes. But it requires much more mental capacity to process! Every time you read variable names like that, you need to make mental conversion into what it really means. And if there are multiple variables like that in a piece of code, especially with similar meanings, the amount of cognitive load grows exponentially. Why not call variable "timeDelta" in the first place?

  • Outdated comment
// Returns time in UTC
public DataTime getLocalTime() {
    return localTime;
}
Enter fullscreen mode Exit fullscreen mode

The biggest problem with comments is keeping them up to date when code evolves. It often happens that while code is changed, comments are unloved and forgotten. As a result, code might have rich comments that are artifacts of the past, showing what used to be there but no longer present. It is absolutely useless and very harmful.

  • To-Do comment
// This is a temporary hack. Will be removed at the next iteration.
// This code should have never been committed.
Enter fullscreen mode Exit fullscreen mode

Such comment is and indication of a major technical debt. I was surprised to see such thing in a major piece of code that constitutes the core of business logic. Especially, noticing the fact that it has been committed over 8 years ago. We all have deadlines and sometimes taking shortcut is the only solution. But important here is having a discipline and pay off technical debt a soon as possible. Otherwise, there will be other shortcuts applied on top of that shortcut and tech debt will grow like a snowball. Bottom line - To Do items don't belong to code, they belong to a task board.

  • Implementation details comment
// Calculates shortest path between points using
// A* algorithm. It starts with ...
public List<Node> calculatePath(...) {}
Enter fullscreen mode Exit fullscreen mode

Implementations change. Interfaces persists. Using comments to describe implementation details is like planing a time bomb. This comment will become obsolete, it is only a question of when.

  • Misleading comment
// Does one thing
public void doSomething() {
    doTheOtherThing();
}
Enter fullscreen mode Exit fullscreen mode

Such comments claim to clarify what a piece of code does, when in fact it does something else. Usually it is also a leftover comment that was not updated as code evolved.

  • No comments at all
class Record {
    private String marketplaceId;
    private String merchantId;  
    private String isbn;
    private Money retailPrice;
    private List<Schedule> schedule;
}
Enter fullscreen mode Exit fullscreen mode

Sometimes you can stumble upon classes with generic names and a collection of fields that don't tell anything. It is unclear what such classes represent, why they exist and what those fields mean.

  • Documentation generation
/**
 * Sets the value of the field "isbn" to be used for the constructed object.
 * @param isbn
 *   The value of the "isbn" field.
 * @return
 *   This builder.
 */
public Builder withISBN(String isbn) {
  this.isbn = isbn;
  return this;
}
Enter fullscreen mode Exit fullscreen mode

I am likely to be in minority when it comes to comments that are used to generate documentation (for example, JavaDoc). While I can understand the good intention, I find it having a big downside. First, this is just a comment, it is used for generation of documentation, but it still needs to be updated to keep up with code just any other comment does. Second, sometimes such comments grow so big that they hide the actual code. I have seen some PoJos, where documentation comments took 75% of space, making it hard to see the code itself.

Comments that create value

Some comments can useful and bring actual value. They can explain things and help to avoid ambiguity.

  • Clarify the intent
// A database record for a book. Core book entity to be used to persist any
// data associated with a book. 
class BookRecord {...}
Enter fullscreen mode Exit fullscreen mode

Such comment does not go deep into details, it does not say anything about implementation or type of database to be used, or about class internals. All those things might and will change. Comment will stay relevant, its goal is to clarify why this class exists, not what it does or how.

  • Provide meaning
// Book discounts. Maps book ISBN -> discounts available for the book
Map<String, List<Discount>> discounts;
Enter fullscreen mode Exit fullscreen mode

Programming languages give limited tools to express the full spectrum of human ideas. Sometimes comments can be used to express ideas that we cannot express in code. For example, using String as a map key in example above does not give any context. But adding "ISBN" as its meaning makes it easier to build mental model. This in turn, facilitates development process and helps to avoid errors.

Start with why

There are multiple opinions on code comments. But let’s ask ourselves the most important question. Why do we write code comments? What is their function, why they exist? My own answer is that comments exist to express ideas that we cannot express with code. It is easy to express algorithms in code, states, sequences. In other words, it is easy to express what to do. Also it is easy to express how to do things. But what programming languages don’t allow us, is to tell why things exist. Why do we do things one way and not another. If we look back at examples above, it is easy to see a pattern. Comments that bring value and stay relevant over time complement code, helps to express ideas. Say why. On the other hand, the comments that bring nothing, or bring more harm than good, get into implementation details, express things that we express with code. In other words, they say what and how.

I believe that the subtle art of writing good code comments that live long and bring value, is to use comments to say why, not what or how.

Summary

Comments cheat sheet

💖 💪 🙅 🚩
igorfil
Igor Fil

Posted on December 28, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related