Comments are always failures.

What a perfect example of a wise sentence (by Uncle Bob) that feels wrong when taken out of context.

(If you’re interested, please read the bigger context here or address your questions to immortal “Clean Code”).

Let me advocate this idea. It was one of the most seditious thought I took away from the book. And yes, I used to be pretty wrong about it, so this gives me an opportunity to confess.

Always failures, huh?

Comments are always failures.

What most people hurt in the sentence is the word “always“.

What stands out for me in the sentence is the word “failure“.

That “always” makes it sound like a dogma, sort of discredits the whole sentence. There’s nearly no place for “never” or “always” in the real world. I assume it was used for marketing purposes, and I don’t take it literally.

The main point, however, is that a comment is a failure. Something wrong. Something that one should not be proud of.

“Always failure” doesn’t mean “don’t ever do that”. Just treat it as something inconvenient. Last resort kind of feature. The lesser evil.

One could rephrase the idea as: “The best code is the one without any comments”. And – hey, I’ve been there. Today I’m hopefully a little smarter. Closer to the point is “The best code is the one that doesn’t need any comments”. That’s a subtle difference. This point of view makes it much easier to explain to other people the idea of comments as a failure.

Why comments are bad

Uncle Bob says that “developers can’t realistically maintain comments”. I can dig it. Comments are non-code, so maintaining them is a different skill than maintaining the code.

From my experience, too frequent comments don’t play their role. They don’t bring value, they only make noise.

If there are no better available options, then fine – let’s write a comment. Comments are allowed. But let’s not pretend it’s as good a solution as their alternatives.

Put another way, a comment is a code smell. There’s this voice in my head that keeps asking after writing a comment:

Pssst, Bartek, have you just written a comment? Are you sure there is no better way to express yourself? You know, in the code. Code doesn’t lie. Non-code sometimes does.

And guess what, I usually find a better way. Oftentimes you can literally incorporate words used in a comment to your code.

Seeing a comment as something bad will force yourself to solve clarity problems in the code if possible. Your code will be telling a story itself, rather than be explained by the prose around it. The idea sounds cool, what about the reality?

Alternatives to comments

The programming world would be a better place if people, before writing a comment, at least considered the following options:

(examples are taken from the Redmine codebase)

1. Rename

# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
  set_autologin_cookie(user)
end

All the information from the comment is literally written in the following code. Except one – the code generates the key. How about:

if params[:autologin] && Setting.autologin?
  generate_key_and_set_cookie(user)
end

or arguably better, but requires slightly deeper refactoring:

if params[:autologin] && Setting.autologin?
  set_autologin_cookie(user.generate_autologin_token)
end

2. Extract a variable / constant

# reserved words
validates_exclusion_of :identifier, :in => %w( new )

In this case, the comment usually suggests the name of a new variable/constant:

RESERVED_WORDS = %w( new )
validates_exclusion_of :identifier, :in => RESERVED_WORDS

3. Extract a method

# Remove email address in usernames
fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }

Again, the name is usually on the plate:

fields = remove_emails(fields)
(...)
def remove_emails(enumerable)
  enumerable.collect {|c| c.gsub(%r{<.+@.+>}, '') }
end

4. Split a line into multiple ones

# Sends an email to the administrators
Mailer.account_activation_request(user).deliver

How about:

email_to_administrators = Mailer.account_activation_request(user)
email_to_administrators.deliver

5. “Turn the tables”

Uncle once again:

So when you find yourself in a position where you need to write a comment, think it through and see whether there isn’t some way to turn the tables and express yourself in code.

This term reminds me that there could be a neat way to express myself in the code that requires more effort. Think this comment:

# Overrides Builder::XmlBase#tag! to format timestamps in ISO 8601
def tag!(sym, *args, &block)

The author explains why inheritance is used. If you’re like me and you watched Nothing is Something by Sandi Metz, you see that this usage of inheritance is doubtful. One could probably use composition over inheritance. Different timestamp format could be expressed explicitly, rather than by overriding a method.

6. Use more precise words

# Creates a new page or updates an existing one
def update

What about:

def upsert

One of the reasons why I like code reviews is that you can learn such things from other programmers for free :).

7. Write a test case

# Loads the default data
# Raises a RecordNotSaved exception if something goes wrong
def load(lang=nil, options={})

At the first glance – a fair warning. But what if you write a test case to show what exactly “something” is?

Other candidates for being replaced by a test case are comments with example usage of a method:

# Examples:
#   issue.workflow_rule_by_attribute # => {'due_date' => 'required', 'start_date' => 'readonly'}

8. Leverage convention

class AccountController < ApplicationController
(...)

  # Login request and validation
  def login
  (...)

  # Log out current user and redirect to welcome page
  def logout

This is an AccountController. What if we leverage the convention and implement login and logout features as a Rails resource, say session? Creating and deleting a session should say it all.

9. Trust your IDE

# Overrides watcher_user_ids= to make user_ids uniq
def watcher_user_ids=(user_ids)

RubyMine indicates it whenever a method is overridden. I’m pretty sure other tools can do this too.

We have 2018. We developed such powerful tools, it would be a shame not to leverage them. Using decent tools and machines saves us from many tedious tasks, why not give them this one job too? Especially if we know for a fact that everyone in the project uses the same IDE.

To be fair – this kind of argument doesn’t work in open source environment like Redmine, where you aim for as many people as possible to work on your code. My point, however, is that such comments bearly give any value to some of the programmers, to the rest they bring questionable value. What comments always do is add noise.

10. Give up the comment

# Find the current user
User.current = find_current_user

Seriously?

A good comment

On the flip side, I found an example of a good comment, explaining “why” something happens. I don’t think there’s an alternative to it.

# Redmine uses 1..7 (monday..sunday) in settings and locales
# JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0
start_of_week = start_of_week.to_i % 7

Wrap up

3 rules of how I understand “Comments are always failures”:

  1. If you write a comment explaining “what” (what is going on in the code) rather than “why”, there’s a great chance you can achieve the same result with a different approach.
  2. You can think of your comment as necessary only if you tried every other way of explaining the code and they fail or don’t suit.
  3. Comments aren’t bad per se. What’s bad is the reason why you need comments in your code.