79

First of all, in this question I'd like to stay away from the polemic on whether source code commenting is good or bad. I'm just trying to understand more clearly what people mean when they talk about comments that tell you WHY, WHAT or HOW.

We often see guidelines like "Comments should tell you WHY; code itself should tell you HOW". It is easy to agree with the statement on an abstract level. However, people usually drop this like a dogma, and leave the room without further explanation. I've seen this used in so many different places and contexts, that it looks like people can agree on the catchphrase, but they seem to be talking about different things entirely.

So, back to the question: if comments should tell you WHY, what is this WHY we are talking about? Is this the reason why that piece of code exists in the first place? Is this what that piece code should be doing? I would really appreciate if someone could give a clear explanation, and then add some good examples (bad examples are not really needed, but fell free to add them for contrast).

There are many questions on whether comments are good or bad, but no one that addresses the specific question of what are good examples of comments that tell you WHY.

4
  • 36
    Sometimes the best comments address WHY NOT. I once encountered a complex bit of code that looked like it could be easily simplified. The comment explained why that obvious simplification didn't work in this particular instance (because the original developer already tried it). Aug 9, 2013 at 14:14
  • 6
    There are many questions on whether comments are good or bad, but no one that addresses the specific question of what are good examples of comments that tell you WHY. If everyone provides a valid example, then they are all correct answers. The format of this website is to facilitate a Q&A process where not all answers are created equal. Aug 9, 2013 at 16:14
  • Good point, @david-kaczynski. What do you suggest?
    – rick
    Aug 9, 2013 at 17:56
  • 1
    Off the top of my head, I can't think of a way to phrase the question so that a single example or generalized tactic can be the "best" answer. There is a chat portion of p.se: chat.stackexchange.com/rooms/21/the-whiteboard, but there would probably be a better forum out there for your question as it is. In all fairness, it looks like your question is receiving a positive response from the community here, so it's probably not worth worrying about. The best piece of advice I can give for finding examples of useful comments would be browsing popular public git repositories. Aug 10, 2013 at 14:17

13 Answers 13

62

The most common and most distinctive example is comments around various workarounds. For example this one:

https://github.com/git/git/blob/master/compat/fopen.c:

/*
 *  The order of the following two lines is important.
 *
 *  FREAD_READS_DIRECTORIES is undefined before including git-compat-util.h
 *  to avoid the redefinition of fopen within git-compat-util.h. This is
 *  necessary since fopen is a macro on some platforms which may be set
 *  based on compiler options. For example, on AIX fopen is set to fopen64
 *  when _LARGE_FILES is defined. The previous technique of merely undefining
 *  fopen after including git-compat-util.h is inadequate in this case.
 */
#undef FREAD_READS_DIRECTORIES
#include "../git-compat-util.h"

You'll surely find more examples in Git and Linux sources; both projects try to follow this rule.

I also recommend to follow this rule even more strictly with commit logs. For code comments it may happen that you fix the code, but forget to update the comment. With the amount of code in usual project, it is guaranteed to happen sooner or later. On the other hand the commit log is tied to the particular change and can be recalled using the "annotate"/"blame" functionality of the version control system. Again Git and Linux have some good examples.

Look e.g. at this commit. (not copying here, it's too long). It has four paragraphs taking almost whole page (and a bit over screenful) describing what exactly was wrong and why it was wrong and than goes on and and modifies all of whoping SIX lines. They use comments like this for two purposes:

  1. All submitted changes are reviewed and the commit log is what has to explain the change to the reviewer.
  2. When a bug is found, the relevant logs are retrieved using "pickaxe" or "blame" to avoid reverting to earlier also incorrect behaviour.

(note: it took me at most 10 minutes random browsing of the git repo to come up with these two examples, so it would sure be easy to find more there)

30

A comment that tells you why explains the reasoning behind the code - for example:

// We need to sync the values if the temp <doodad> GUID matches one of the active <doodad>'s
// GUID, as the temp <doodad> has the most recent values according to the server and said 
// values might have changed since we added the <doodad>. We want a user to be able to <foo> 
// the <doodad> whenever, which means those values must be accurate.
for (doodad in doodads) {
    if ([doodad guid] == [tempDoodad guid]) {
        [doodad updateFromDoodad:tempDoodad];
        break;
    }
}

A comment that tells you how explains what the code does.

// Loop through our <doodads> and check for a GUID match. If it matches, copy the new values
// on the <doodad> that matches 
for (doodad in doodads) {
    if ([doodad guid] == [tempDoodad guid]) {
        [doodad updateFromDoodad:tempDoodad];
        break;
    }
}

The difference is a maintainer can look at the first one and say, "Oh, so this might be out of date!" In the second case, said maintainer has a comment that doesn't tell you anything the code itself doesn't reveal (assuming good variable names).

Here's a real life example of a why comment, from some iOS code I worked on where we needed to get a gateway address (or reasonable guess for it). I could have just left the comments that said things like "Initialize the receive socket," but that would only tell a maintainer (or future me) what was happening, not why I had to do this strange kludge to get the gateway address in the first place.

/*
 We're going to do something really hacky here and use a custom partial
 implementation of traceroute to get our gateway IP address.

 [rant removed - irrelevant to the point]

 There's no good way to get at the gateway address of an iDevice
 right now. So, we have two options (per https://devforums.apple.com/message/644915#644915 ):
 1. Get at and parse the routing table (like netstat -rn, or route -n)
 2. Do a traceroute and grab the IP address for the first hop

 As far as I can tell, the former requires <sys/route.h> from the Mac OS X
 header files, which doesn't seem like a good idea to me. Also, there's a
 thread on the Apple Developer forums that seems to imply that header isn't
 in iOS for a reason (https://devforums.apple.com/message/774731#774731 ).

 So when we send our request with a TTL of one it will survive a single hop
 to the router and return, triumphant, with the router's IP address!

 Viva la kludge!

 PS: Original source was the below SO question, but I've modded it since then.
 http://stackoverflow.com/questions/14304581/hops-tracing-ttl-reciveform-on-ios/14304923#14304923
 */

// Default to using Google's DNS address. We used to try checking www.google.com
// if reachability reported we had internet, but that could still hang on routers
// that had no internet connectivity - not sure why.
const char *ip_addr = [kGoogleDNS UTF8String]; // Must be const to avoid undefined behavior
struct sockaddr_in destination,fromAddr;
int recv_sock;
int send_sock;

// ... more code follows
3
  • 4
    The first example is excessively verbose and includes much of the "how". It shoud say just "Update the <doodads> from temp <doodad> so user can safely <foo> it whenever." The rest is trivial to imply from this or the code. Also the "fairy tale introduction" in the first four paragraphs of the last example is totally pointless. I'd leave "Viva la kludge!"; it's funny and it's in the end. But the beginning is just too many words one has to dig through before getting to the actual explanation.
    – Jan Hudec
    Aug 9, 2013 at 15:03
  • @JanHudec Adjusted as per your feedback. Look about right?
    – thegrinner
    Aug 9, 2013 at 15:38
  • 15
    One of the nice things about the second example is that it not only explains why the code works a particular way, but also explains why other, reasonable alternatives were not taken. This makes the code much more maintainable, since the next guy who reads the code and thinks, "Why can't I just parse the routing table?" can just read the comment. Further, someone who does come up with a legitimate reason to change the code will be more confident that it is safe to do so. Otherwise, a maintainer is left afraid that any change will fail in the (unknown) scenario which inspired the kludge.
    – Brian
    Aug 9, 2013 at 20:15
18

I would like to start my answer with a quote made by Jeff Atwood in his Blog post Code Tells You How, Comments Tell You Why:

the best kind of comments are the ones you don't need

He also states that:

You should first strive to make your code as simple as possible to understand without relying on comments as a crutch. Only at the point where the code cannot be made easier to understand should you begin to add comments.

I totally agree and at this point I must add that before I can start making the code as simple as possible I make the code work and then start refactoring. So during the first run before refactoring adding why comments helps a lot.

For example if using 3 nested loops with 2 dimensional hashtables to fill a table of weekdays while parsing data, it's very easy to lose track of what was done by someone or even yourself if not looked at for a few weeks and suddenly refactoring.

[loop1]6oclock -> [loop2]Monday -> [loop3]stage 1 to 4
         -> tuesday-> stage 1 to 4
         ...
         -> Saturday -> stage 1 to 4
    7oclock -> Monday-> stage 1 to 4
        ....etc.

The upper being an example of how 3 nested loops would work before refactoring.
Also explaining some branch conditions can help understand code much better with what one was thinking in the process:

// added a zero before the actual day in order for the days always to be 2 digits long.
if( actualDayFuture < 10 ) 
{ 
     actualDayFuture = padIfSingleDigitDate(actualDayFuture); 
}

Even simple and obvious code works well with comments. Just to make things a little more obvious, clearer or easier to understand for colleagues and even for yourself in maintaining software.

Sure xp states to have code that is self explaining, but does a one line comment hurt?

I also find the following rules from this blog to be very helpful:

  • Understand the material before you write
  • Write as though your audience is a fourth grader
  • Think about how readers might misinterpret you

Anyone who has to get back into their own code or someone elses or even legacy code knows that it can be a headache. So instead of being lazy or trying to be an uber-coder in not commenting anything or very little, why not make your own or some poor bugger's, who has to maintain your code, future life much easier by following the rules quoted.

Also many programming descisions made are doubted during reviews and it isn't always clear why some parts were written as they were even if some sections of code are vital for a programm to work due to a major bug found as the code was used over years. So in order to not bore you all completely with a tl;dr close with a last quote from acmqueue:

Prior, clear, and extensive documentation is a key element in creating software that can survive and adapt. Documenting to high standards will decrease development time, result in better work, and improve the bottom line. It’s hard to ask for more than that from any technique.

10
  • 8
    In your second example, one could eliminate comments altogether by refactoring: actualDayFuture = padIfSingleDigitDate(actualDayFuture); This is trivial, but a more robust example would benefit from this approach. Aug 9, 2013 at 12:46
  • 4
    I would have moved the conditional into the method as well. -- Again, not for something so trivial, but it allows me to disregard thinking about padding logic altogether. I wouldn't replace your original example though, as it's a better answer to the question. It's more of a side note, exploring other alternatives. Aug 9, 2013 at 12:58
  • 1
    Ad "Sure xp states to have code that is self explaining, but does a one line comment hurt?": Comments are good, but there is also a danger of over-commenting. Every line of comment is one that somebody may forget to update when they change the code.
    – Jan Hudec
    Aug 9, 2013 at 14:15
  • 1
    A better way to say this is "The best kind of comment is the absence of the need for a comment". Comments that are not needed (but are written anyway) are not good comments.
    – Kaz
    Aug 10, 2013 at 0:36
  • 1
    Interesting that the referenced code int directionCode = (x > oldX) ? DIRECTIONCODE_RIGHT : (x > oldX) ? DIRECTIONCODE_LEFT : DIRECTIONCODE_NONE; is in error. Certainly should be ... (x < oldX) ? DIRECTIONCODE_LEFT : DIRECTIONCODE_NONE;. Good comment ideas - bad code. Sep 9, 2015 at 22:23
8

I tend to reduce comments to either references where a certain functionality/code is explained more thoroughly or to explain why a certain way of programming is chosen.

Considering that other programmers with similar skills use or read your code, it is important to comment if you use a different-than-expected way of achieving something. So you can explain in a comment why you choose this way.

For instance, if you can make use of two different sensors on an Android device and one of them does not suit your needs, you can explain in the comment why you chose the other one.

So the 'why' should give a rationale on your choices made.

1
  • 5
    References are a great example. //This method uses the furshclingeheimer algorithm to ronsterize the foobit. See http://... Aug 9, 2013 at 12:49
8

Comments should tell you what the code does not, not necessarily deliniated by WHY, HOW, or WHAT. If you have good names and have well delineated functions, it is quite possible that the code can tell you exactly what is going on. For example:

List<LightMap> maps = makeLightmaps(receivingModels);
TrianglePartitioner partition = new Octree(castingTriangles);
List<Photon> photons = firePhotons(lights, partition);

if (photons.Count > 0)
{
      PhotonPartitioner photonMap = new KDTree(photons);
      gatherPhotons(maps, photonMap, partition, lights);
}

This code really does not need comments. The function and type names make it easy to understand.

Sometimes, however, it can be difficult or impossible to really make fluent code like the above. For example, the next code snippet is for finding a statistically random point on a sphere. The math is pretty opaque, so a comment with a link to the explanation is to help tell HOW it works. This can be wrapped in a function to tell WHAT it does without needing a comment if needed more than once, otherwise the link title also helps in that department.

double randomA = localGenerator.NextDouble();
double randomB = localGenerator.NextDouble();

//http://mathworld.wolfram.com/SpherePointPicking.html
double theta = 2 * Math.PI * randomA;
double phi = Math.Acos(2 * randomB - 1);

Vector3 randomDirection = new Vector3(Settings.ambientRayLength * (float)(Math.Cos(theta) * Math.Sin(phi)),
                                      Settings.ambientRayLength * (float)(Math.Sin(theta) * Math.Sin(phi)),
                                      Settings.ambientRayLength * (float)Math.Cos(phi));

Another example of when comments tell you what the code does not is for explaining a decision. In the next example, the code does not lock a non-thread-local variable inside a threaded piece of code. There is a reason for this and the comment explains WHY. Without the comment, it might be considered a bug, or just not be even noticed.

Random random = new Random();
Parallel.For(0, maxPhotons, delegate(int photonIndex, ParallelLoopState state)
{
    ...
    //I don't actually care if this random number is unique between threads, threadsafty is not that big of a deal
    //  in this case and locking the random object could cause a lot of lock contention
    while (random.NextDouble() > reflectProbability)
    {
        ...
    }
    ...
}

It could, perhaps, be improved to say why the random object is not created inside the parallel loop in the first place. If there is no reason, it could also make someone come along and realize that the whole idea is stupid and is a good place for refactoring.

2
  • Is it reasonable to describe code as not needing comments when the comments are preceded by WriteText rather than //?
    – user25946
    Aug 9, 2013 at 17:42
  • 1
    As I said in the answer, comments are unneeded even if there were no print statements, however I have edited it to remove the print statements in order to make the point clearer. Aug 9, 2013 at 20:03
5

It may be helpful to recognize different kinds of "why"--most notably:

  • Reasons that code which seems overly complex would not work if simplified (e.g. a seemingly-superfluous typecast may be necessary to ensure that code works in some corner cases).

  • Reasons that some particular simple operation which looks dangerous is actually safe (e.g. "Our fetch-data routine will report a dummy item item past the last as being less than anything else, and the item after that as being greater; any item which should sort before another, in a consistent ascending or descending sequence, will have at least one more (possibly dummy) item following it").

In many cases, a comment of the second type in one part of the code may "match up" with a comment of the first type in another (e.g. "While it would appear this sequence of operations could be simplified, the Fitz routine relies upon the Wongle not being Woozled until after the Bandersnatch has been Blarped.")

2

Don't forget, if you're writing a program, you're not just randomly typing stuff, you're doing it because you have a model of what you want, whether it's in a formal document or just in your head. Stuff in your head is every bit as real as software/data in a computer (and just as likely to contain bugs).

Someone reading your code may not have that model in their head, so comments can serve to tell them what the model was and how the code relates to it. I think that is what's meant by "why". Certainly it's good to make the code itself as self-explanatory as possible, but that's not always good enough. Example:

// transform the x,y point location to the nearest hexagonal cell location
ix1 = (int)floor(0.5 + x + y/2);
iy1 = (int)floor(0.5 + y);

On top of that, the model changes over time, and those changes have to be transferred to the code. So the comments need to not only say "why" something is in the code, but just as important how to change it in response to anticipated model changes. Example:

// to change to square cell locations, remove the "+ y/2" in the above code

I think that purpose for comments is sometimes neglected.

4
  • 2
    The question is asking for examples. Could you add an example to make this answer more useful? Aug 9, 2013 at 13:46
  • 2
    The first chunk of code looks like a classic example of explaining "what," to me. Not that it's a bad comment, but I don't think it answers the OP's question.
    – user25946
    Aug 9, 2013 at 17:46
  • @Jon: If the comment were not there, the reader can see what is happening, but has no idea why. Aug 15, 2013 at 12:10
  • 1
    @MikeDunlavey: I disagree. I still have no idea - why do you want the nearest hexagonal cell location? What is the purpose of getting this location? Would it affect anything if I deleted these two lines?
    – user25946
    Aug 15, 2013 at 14:36
2

Not all my comments are of the 'why' type, but many are.
These are examples from one (Delphi) source file:

// For easier access to the custom properties:

function GetPrivate: Integer;   // It's an integer field in the external program so let's treat it like that here

// The below properties depend on the ones above or are calculated fields.
// They are kept up-to-date in the OnEventModified event of the TTSynchronizerStorage
// or in the ClientDataSet.OnCalcFields of the TcxDBSchedulerStorage.DataSource.DataSet
property IsModified       : Boolean   read GetIsModified   write SetIsModified;
property IsCatTT          : Boolean   read GetIsCatTT      write SetIsCatTT;
property IsSynced         : Boolean   read GetIsSynced     write SetIsSynced;

lLeftPos := pos(' - [',ASubject); // Were subject and [shiftnaam:act,project,cust] concatenated with a dash?

// Things that were added behing the ] we will append to the subject:

// In the storage the custom value must also be set for:
Self.SetCustomFieldValueByname(cCustFldIsCatTT,Result);

// When we show the custom fields in a grid, the Getters are not executed,
// because the DevEx code does not know about our class helpers.
// So we have two keep both properties synchronized ourselves:

// lNewMasterEvent was set to usUpdated, overwrite because we added:
if ARepair then
  lNewMasterEvent.CustUpdateStatus := usRecreated

// The source occurrence date may have bee changed. Using GetOriginalDate we can retrieve the original date,
// then use that for creating a target occurrence (and update its date):

lNewTTOccurrence.CustSyncEntryID := cSyncEntryID0;    // Backward compatibility with old sync methode

// Single event became recurring or vice versa; replace entire event

// In contradiction to CopySingleEventToTimeTell, CopyMasterEventToTimeTell does not have a ANewStatus parameter
// because master events are always added.

Note that (my) why comments usually precede the code that is going to do it (hence end with a colon).

I do some have comments explaining only what is happening, e.g. when a process has many steps which have a logical grouping (and the code is not refactored to show that automatically), I will comment like:

// Step 1. Initialization
1

I understand the WHY as being the reason why you do something in a possibly strange or perhaps illogical way, due to the given circumstances requiring it to be done so. The HOW can be seen in the code itself, no matter how odd it is, even if the code makes no "sense". The WHAT is probably best told in the beginning of the class/function documentation. So that leaves you with adding the WHY, where you explain anything not included in the HOW and WHAT, and the peculiar ways you need to take due to reasons beyond your control.

Of course it's not always the case, outside the land of unicorns and rainbows...

HOW:

foreach($critters as $creature) {
   $creature->dance();
}

WHAT:

/* Dancing creatures v1.0
 * 
 * The purpose of this is to make all your critters do the funky dance.
 */

foreach($critters as $creature) {
  $creature->dance();
}

WHY:

// We had to store the items in an array of objects because of _____ (reason)
foreach($critters as $creature) {
   $creature->dance();
}
4
  • 5
    how does this answer the question asked?
    – gnat
    Aug 9, 2013 at 10:18
  • 1
    To quote OP: "So, back to the question: if comments should tell you WHY, what is this WHY we are talking about?", and I answered that question: the WHY that is talked about is the reasoning for the existence of the given piece of code. Aug 9, 2013 at 10:21
  • 1
    The question specifically asks for examples a couple of times. Could you add an example to this answer to make it more useful? Aug 9, 2013 at 13:48
  • 1
    I don't think either of these comments are actually helpful. If your function's signature was critters.dance(), then the comment just repeats the obvious, and "We couldn't get it to work with any other way we tried" is completely unhelpful. Also, saying "we will call the method for each object" is repeating what the code very clearly says. Aug 9, 2013 at 23:16
1

I learned to ALWAYS write comments in C++ headerfiles (since it is not always clear WHAT a function does, even though the name gives a good hint) especially if you pass an API on to other developers or use a tool for autodoc like doxygen.

So to me a typical comment looks something like

/*** Functionname
/*   What happens here
/*  [in] Params
/*  [out] params
/*** 

The only time I used WHY comments is stuff that is hard to grasp and sometimes even to the programmer, like "DO NOT TOUCH THIS! Because ..." or "PROGRAMM WILL CRASH IF LINE IS DELETED..."

Workarounds, hacks and strange behaviour qualifies for WHY criteria in my eyes...

A very good and even hilarious example is this "workaround" for some messed up code written by some person named Richard, someone else wrapped it and explained why in the comments... https://stackoverflow.com/a/184673/979785

Unfortunately there are quite a few times, where you are forced to wrap bull**** because you can not touch the original, either because "it has always been that way" or you do not have access or... well, you do not have the time to fix the original for the purpose doesn't really qualify for the overhead.

8
  • 7
    Except that the question is about comments, not documentation. They are actually different things (the documentation tag is regrettable but still doesn't apply to the question).
    – Thomas
    Aug 9, 2013 at 11:53
  • Well excuse the fact, that in my native language comment and documentation comment are used interchangeably and so with the tag I assumed it was applicable for this question aswell. Is that really a reason to downvote?
    – AnyOneElse
    Aug 9, 2013 at 13:23
  • 2
    The question asks a couple of times for examples of why comments, but the only example you include is a what comment. People skimming the answers for examples might be mislead by your example. Can you give an example of a why comment? Aug 9, 2013 at 13:50
  • although I said there are very few WHYs in my code, and I named two examples: EDITED... here's a link, that definitely qualifies for a WHY
    – AnyOneElse
    Aug 9, 2013 at 14:07
  • @AnyOneElse I did not downvote. It was there before I arrived.
    – Thomas
    Aug 9, 2013 at 14:44
0

Code is supposed to specify execution plan. That way the program follower (or the compiler) can figure out what to do, and how to do it. The what is broken down into steps that the program follower can follow. The primitive steps are the how.

The intent of the coder is another matter. In simple, clear, straightforward code the intent is obvious. Any reasonably proficient human reader will arrive at the intent of a block of code, just by reading the code. Most code should read like this.

Occasionally, the relationship between intent and plan is obscure. The code reveals the what and the how, but not the why. That's when comments that reveal the intent are worthwhile. The programer's intent is the why.

1
  • 3
    The question asks a couple of times for examples. Can you add an example to your answer to make it more useful? Aug 9, 2013 at 13:52
0

Having this issue right now wading through stored procedures and views against a complex and somewhat convoluted data model.

We have (numerous) made up selects like "Case when x.account is not null and x.address in (select address from fedex) then x.account else y.account end" throughout and productivity is expected though there is not time at all to read all the source code. And this example kindof sort of makes sense sort of, but it's still inscrutable.

The comments explaining why if in fedex then x and if not then y -- sheds light on the entire system and when we read enough of them we start to get it. And this is over simplified and there are hundreds or thousands of similar statements.My heart glows warmly towards whoever the kind dev from 2007 was who put in those why's.

So yeah, complex convoluted data models and hairy veiws and stored procedure with multiple validly named paths, please for the love of G-d tell us why.

0

I just wrote this comment; it's a concrete example of explaining why a line of code is what it is, and in particular why I changed it.

The method examines stored data and assesses whether it's complete through the present day on the one end, and through the start date on the other end.

// In principal, this should be ">=", as we may have data up to the account start
// date but not complete for that day; in practice, 98% of the time if we have
// data for the start date it *is* complete, and requerying it would be a waste
// of time.
while (endDate > accountStartDate)
    ...

As you can probably guess, the greater-than operator had been a greater-or-equal. The comment explains why the old value makes sense and why the new value is better. If anyone looks at this in the future, they'll see that the use of ">" is not an oversight, but an optimization. They can then change it or leave it, based on the need at that time.

Not the answer you're looking for? Browse other questions tagged or ask your own question.