Functions for Documentation Instead of Comments

Dec 23, 2019

Programming

By: Brandon Quakkelaar

Using good function naming and composition will decrease the need for gratuitous comments and increase your code’s readability.

I’ve become convinced that comments are often worth avoiding. Comments aren’t subject to a compiler nor to an interpreter. Code may change and the corresponding comment often remains as it was. Except now the comment is misleading. This makes maintaining the code unnecessarily difficult.

Use Function Names to Document Code

Consider this code that determines the “Day of the Programmer”. The GetDay method (method being a function on an object) gets the date that represents the 256th day of the year. And, it accounts for when the calendar changed from Julian to Gregorian.

public static DayOfTheProgrammer 
{
  public static string GetDay(int year)
  {
    var september256Day = 13;

    // Find whether the year is missing 14 days.
    if (year == 1918)
    {
      september256Day = 26;
    }
    // Find whether it's a leap year
    else if (year >= 1918 && (year % 400 == 0 || year % 4 == 0 && year % 100 != 0) || year < 1918 && year % 4 == 0)
    {
      september256Day = 12;
    }

    return $"{september256Day}.09.{year}";
  }
}

The above code works. But it is difficult to read. The programmer has considered future maintainers by adding explanations in comments above each if statement. That is a step in the right direction, however comments are particularly vulnerable to “code rot” since the compiler (or interpreter) is unaware of their content. As the code is maintained it’s easy for neglected comments to have misleading or even false information in them. I prefer using descriptive names in lieu of comments.

public static class DayOfTheProgrammer
{
  public static string GetDay(int year)
  {
    var september256Day = 13;

    if (IsMissing14Days(year))
    {
      september256Day = 26;
    }
    else if (IsLeapYear(year))
    {
      september256Day = 12;
    }

    return $"{september256Day}.09.{year}";
  }

  private static bool IsMissing14Days(int year)
  {
    return year == 1918;
  }

  private static bool IsLeapYear(int year)
  {
    return year >= 1918 
      && (year % 400 == 0 || year % 4 == 0 && year % 100 != 0) 
      || year < 1918 
      && year % 4 == 0;
  }
}

Refactoring the code into functions with meaningful names has eliminated the need for the original comments. The same information is being communicated in both versions of the code. However, the original is vulnerable to comment rot while the refactored code that uses descriptive names is easier to maintain.

The programmer may decide to refactor further. The conditional statement in IsLeapYear is still pretty long and requires effort to read and understand. We can add documentation by using descriptive variable names.

private static bool IsLeapYear(int year)
{
  var isGregorianCalendar = year >= 1918;
  var isGregorianLeapYear = year % 400 == 0 || year % 4 == 0 && year % 100 != 0;

  if (isGregorianCalendar && isGregorianLeapYear)
  {
    return true;
  }

  var isJulianCalendar = !isGregorianCalendar;
  var isJulianLeapYear = year % 4 == 0;

  return isJulianCalendar && isJulianLeapYear;
}

(For more information about the Day of the Programmer problem visit HackerRank.)

Prefer Smaller Functions

Long functions typically suffer from multiple code flows, varying levels of abstraction, and they often have “sections” delineated by comments. If the programmer feels the need to use comments to separate a program into sections, that could be an opportunity to write smaller functions with descriptive names.

The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.

— Clean Code by Robert C. Martin (pp. 34)

Useful Comments

Though I believe that comments are often worth avoiding, there are times when comments are useful.

Useful comments often fall into one of the following categories.

Legal comments such as copyright and license information.

Explanation of Intent

Good comments can explain the programmer’s intention rather than explaining the implementation. Don’t explain how it works, rather explain the reason why it works that way.

Warning

Comments can be useful for communicating warnings about possible consequences. Things like // takes a long time to run, or // not thread safe are good to know and difficult to document without a brief comment.

If a comment falls outside of the previous three categorizations, explore the idea of removing it. The message might be a good candidate for documenting with function names, or variable names.