Tuesday, December 26, 2017

A Log Message is Executable Code and Comment

Although there are differences of opinion regarding how many comments one should add to one's code, I think it's safe to say most developers would agree that the comment in the following code snippet is superfluous:

// increment the total
total++;

In that example, the code is simple and really is self-explanatory with a decently named variable total being incremented with a standard Java operator. Fortunately, I don't see that obviously unnecessary type of comment as much now as I used to.

One area in which I still seem redundant comments more commonly than I'd like is associated with code situations that lead to explanatory log statements. Especially when the situation that leads to the log statement is a bit tricky, there occasionally seems to be a desire to write a comment to the developer who will be reading and maintaining that code in the future along with a desire to log relevant information for use in debugging the special condition later. In most of these cases, a well-crafted log message (like other well-crafted executable code) can speak for itself and doesn't require additional commentary.

Although writing logging code that is self-documenting is largely similar to writing any executable code that is self-documenting, logging code offers the advantage of being able to express arbitrary details within a log message. Normal code is limited by the constructs supported by the programming language and there may be times when the language's constructs are not as conducive to expressing intent as one would like. Logged messages are far less restrictive in terms of what can be articulated. On the downside, logged messages are often more likely to be neglected when changes to the code are made. Code changes must be made, but often log messages can remain unchanged (even if they should have been changed) and this omission may not be noticed until the statement is logged at some point in the future. Still, logged messages have a better chance of being changed/updated than do comments, which are only exposed during code reading.

An incidental benefit of using log messages to express special conditions instead of code comments is that this can lead to greater discipline in writing concise but thorough log messages. Another benefit of "commenting" via logged messages rather than in-code comments is that the messages can be written at runtime when the situation occurs and provide valuable insight into the code's behavior that was simply not available when analyzing the static code.

The following are two code listings, one using an in-code comment and the other using logging, to express the same sentiment to developers maintaining this code in the future. In both cases, the business logic consideration being documented is that the National Football League (NFL) Super Bowl in 2016 (won by the Denver Broncos) was not named with the traditional Roman Numeral naming convention. Instead of being named "L" as was anticipated based on earlier Super Bowls, this one was named "50". This is a contrived example of the type of business logic rule that is often expressed as a comment in code. Line 10 is the focus of each code listing here.

private int convertToSuperBowlYear(final String superBowlNumber)
{
   int superBowlYear;
   if (superBowlNumber == null || superBowlNumber.isEmpty())
   {
      superBowlYear = 0;
   }
   else if (superBowlNumber.equals("L"))
   {
      // Super Bowl 50 was not named with the conventional Roman Numeral, so using '50' instead of 'L'
      superBowlYear = 2016;
   }
   else
   {
      superBowlYear = getSuperBowlYearFromNumber(getDecimalNumber(superBowlNumber));
   }
   return superBowlYear;
}
private int convertToSuperBowlYear(final String superBowlNumber)
{
   int superBowlYear;
   if (superBowlNumber == null || superBowlNumber.isEmpty())
   {
      superBowlYear = 0;
   }
   else if (superBowlNumber.equals("L"))
   {
      logger.fine("Super Bowl 50 was not named with the conventional Roman Numeral, so using '50' instead of 'L'.");
      superBowlYear = 2016;
   }
   else
   {
      superBowlYear = getSuperBowlYearFromNumber(getDecimalNumber(superBowlNumber));
   }
   return superBowlYear;
}

Implementations for methods getSuperBowlYearFromNumber(int) and getDecimalNumber(String) are not shown here because they are not important to this discussion. The important thing here is that "L" is not a valid Super Bowl number and so "50" must be used instead of "L" when determining the year of the Super Bowl. A developer unfamiliar with the NFL or it Super Bowl naming convention and its departure from that convention for the 2016 Super Bowl need some type of comment to understand why one Super Bowl is treated differently than the others.

As a side note and speaking of Roman numerals, it is a bit surprising how many Java code examples are on the web for converting between Roman numerals and decimal-based integer. Some of these include Converting Roman Numerals To Decimal, Converting Roman Numerals to Decimal lying between 1 to 3999, Convert Roman numerals to decimal using Java, Convert roman numerals to decimal in java, and How To Convert A Roman Number To Integer. I suspect there are many homework problems inspiring this plethora of code samples.

Alexey has recently published the blog post "Replace TODO comments in your code with WARNING log messages maybe?" in which he states that he has started writing warning and error level log messages in situations in which he formerly wrote "TODO" comments. This is a more specific and more obvious example of using log messages instead of comments. In Alexey's case, he has been doing this because he realized that he "always forget about" the "TODO" comments and they "rarely get found and almost never get fixed." Alexey's conclusion is, "That's why I advise you to try this approach with writing your comments, your thoughts, and even your doubts into the logs: it will help you and can even entertain you and your colleagues!"

There are situations in which what one might add to in-source comments that would not be appropriate to add to log messages. Such situations include high verbosity of comments or sensitive nature of comments. It is also worth noting that some comment-level logged messages might never actually get logged because their log level would be set so specific that the log level would never actually be enabled during code execution. However, there are advantages in many scenarios to using concise but thorough log messages instead of in-code comments for communicating to future developers and your future self.

No comments: