Skip to content

Instantly share code, notes, and snippets.

@prmichaelsen
Last active October 9, 2019 10:15
Show Gist options
  • Save prmichaelsen/bf352eb3e91a3717b200ab0b937f852b to your computer and use it in GitHub Desktop.
Save prmichaelsen/bf352eb3e91a3717b200ab0b937f852b to your computer and use it in GitHub Desktop.

The Myth of Self Documenting Code

Or, how I learned to stop worrying and love the comment.

This article is a response to Joe Eames' article on self-documenting code. If you haven't read it yet or aren't familiar with the concept of self-documenting code, please check out the article here.

I consider self-documenting code to be a myth and perpetuating it to be dangerous (especially to new programmers).

Before we get into why, let me summarize the two primary arguments for sefl-documented code:

  1. Comments that explain what the program are doing are redundant. As code changes, comments can also become out-of-date and stale, making the code harder to maintain overall.

  2. Avoiding comments forces the developer to write better code. For instance, variable and method names must be more descriptive in the absence of comments. And when a function begins to do too much, it must be split into smaller functions with equally good naming in order to be digestible.

I actually agree with these arguments. However, they presume that the programmer will actually write better code if they stop writing comments.

Post hoc ergo propter hoc

More harmful than a well-defined method with an out-of-date comment is a poorly written method with no comments at all. I would almost always have the programmer leave me more clues to their original intention than not. In that sense, one shouldn't argue against comments, but instead advocate for descriptive variable names and unit-testable functions with well-defined and limited function.

Self-documenting code is actually the result of following other coding best practices:

  • give your variables short, but meaningful names
  • write unit-testable functions with a well-define, limited scope and function
  • write idempotent functions without side-effects
  • write unit tests that demonstrate the intended behavior of the code

If you strive for these goals, your code will naturally be self-documenting.

Meanwhile, you should still include comments in order to document things such as the why, how, etc--anything deeper than simply the "what does this code do?". Comments can be useful to serve as an explanation of why this implementation--although it appears non-intuitive--is actually better than the other more intuitive implementations that were already considered by the original programmer.

Consider this snippet, from a related article:

function getParticipantNames() {
    const participants = database.getAllParticipants();

    return participants.map(p => p.name);
}

The author suggests one might ask:

Why is there no function called database.getAllParticipantNames(), to query just the names from the database (instead of all this other data we don't need)?

There could be a million different technical reasons for this, but for this example let's say that the database queries are cached. This means that when the query runs, the received data is stored in memory for a little while so subsequent calls don't need to make another roundtrip to the database. So using the same query here is actually an optimization, even if we get too much data from the call.

Adding a comment helps to explain the why:

function getParticipantNames() {
    // Because queries are cached, using the `allParticipants` query 
    // prevents another roundtrip to the database
    const participants = database.getAllParticipants();

    return participants.map(p => p.name);
}

Well written comments also reduce the overall cognitive load involved with understanding your code. "Why" comments added to small interconnected functions help developers quickly understand the bigger picture of the software more quickly.

In summation, "why" comments can:

  • justify non-intuitive approaches
  • explain tradeoffs of the implementation
  • explain the intent of the programmer
  • reduce the cognitive load of reading code
  • explain the larger picture of how small interconnected pieces fit together
  • provide context to code that may or may not follow best practices

The right tool for the job

In that same sense, however, dogmatically adding unnecessary or "what" comments to satisfy some checkstyle or linter is just as bad as dogmatically avoiding them to satisfy some personal crusade of writing purely self-documenting code.

Ultimately, my goal isn't to push relying too heavily on comments or only writing self-documenting code, but to understand why either is valuable in the first place. In other words, self-documentation and comments aren't inherently at odds. Rather, they have different purposes and work together to make overall better code.

Further reading:

function getParticipantNames() {
const participants = database.getAllParticipants();
return participants.map(p => p.name);
}
function getParticipantNames() {
// Because queries are cached, using the `allParticipants` query
// prevents another roundtrip to the database
const participants = database.getAllParticipants();
return participants.map(p => p.name);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment