Best practices for writing code comments
[Ed. note: While we take some time to rest up over the holidays and prepare for next year, we are re-publishing our top ten posts for the year. Please enjoy our favorite work this year and we’ll see you in 2022.]
Famed MIT professor Hal Abelson said: “Programs must be written for people to read and only incidentally for machines to execute.” While he may have purposely understated the importance of running code, he is spot on that programs have two very different audiences. Compilers and interpreters ignore comments and find all syntactically correct programs equally easy to understand. Human readers are very different. We find some programs harder to understand than others, and we look to comments to help us make sense of them.
While there are many resources to help programmers write better code—such as books and static analyzers—there are few for writing better comments. While it’s easy to measure the quantity of comments in a program, it’s hard to measure the quality, and the two are not necessarily correlated. A bad comment is worse than no comment at all. As Peter Vogel has written:
- Writing and then maintaining comments is an expense.
- Your compiler doesn’t check your comments so there is no way to determine that comments are correct.
- You are, on the other hand, guaranteed that the computer is doing exactly what your code is telling it to.
While all of these points are true, it would be a mistake to go to the other extreme and never write comments. Here are some rules to help you achieve a happy medium:
Rule 1: Comments should not duplicate the code.
Rule 2: Good comments do not excuse unclear code.
Rule 3: If you can’t write a clear comment, there may be a problem with the code.
Rule 4: Comments should dispel confusion, not cause it.
Rule 5: Explain unidiomatic code in comments.
Rule 6: Provide links to the original source of copied code.
Rule 7: Include links to external references where they will be most helpful.
Rule 8: Add comments when fixing bugs.
Rule 9: Use comments to mark incomplete implementations.
The rest of this article explains each of these rules, providing examples and explaining how and when to apply them.
Rule 1: Comments should not duplicate the code
Many junior programmers write too many comments because they were trained to do so by their introductory instructors. I’ve seen students in upper-division computer science classes add a comment to each closed brace to indicate what block is ending:
if (x > 3) {
…
} // if
I’ve also heard of instructors requiring students to comment every line of code. While this may be a reasonable policy for extreme beginners, such comments are like training wheels and should be removed when bicycling with the big kids.
Comments that add no information have negative value because they:
- add visual clutter
- take time to write and read
- can become out-of-date
The canonical bad example is:
i = i + 1; // Add one to i
It adds no information whatsoever and incurs a maintenance cost.
Policies requiring comments for every line of code have been rightly ridiculed on Reddit:
// create a for loop // <-- comment
for // start for loop
( // round bracket
// newline
int // type for declaration
i // name for declaration
= // assignment operator for declaration
0 // start value for i
Rule 2: Good comments do not excuse unclear code
Another misuse of comments is to provide information that should have been in the code. A simple example is when someone names a variable with a single letter and then adds a comment describing its purpose:
private static Node getBestChildNode(Node node) {
Node n; // best child node candidate
for (Node node: node.getChildren()) {
// update n if the current state is better
if (n == null || utility(node) > utility(n)) {
n = node;
}
}
return n;
}
The need for comments could be eliminated with better variable naming:
private static Node getBestChildNode(Node node) {
Node bestNode;
for (Node currentNode: node.getChildren()) {
if (bestNode == null || utility(currentNode) > utility(bestNode)) {
bestNode = currentNode;
}
}
return bestNode;
}
As Kernighan and Plauger wrote in The Elements of Programming Style, “Don’t comment bad code — rewrite it.”
Rule 3: If you can’t write a clear comment, there may be a problem with the code
The most infamous comment in the Unix source code is “You are not expected to understand this,” which appeared before some hairy context-switching code. Dennis Ritchie later explained that it was intended “in the spirit of ‘This won’t be on the exam,’ rather than as an impudent challenge.” Unfortunately, it turned out that he and co-author Ken Thompson didn’t understand it themselves and later had to rewrite it.
This brings to mind Kernighan’s Law:
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
Warning readers away from your code is like turning on your car’s hazard lights: an admission that you’re doing something you know is illegal. Instead, rewrite the code to something you understand well enough to explain or, better yet, that is straightforward.
Rule 4: Comments should dispel confusion, not cause it
No discussion of bad comments would be complete without this story from Steven Levy’s Hackers: Heroes of the Computer Revolution:
[Peter Samson] was particularly obscure in refusing to add comments to his source code explaining what he was doing at a given time. One well-distributed program Samson wrote went on for hundreds of assembly-language instructions, with only one comment beside an instruction that contained the number 1750. The comment was RIPJSB, and people racked their brains about its meaning until someone figured out that 1750 was the year Bach died, and that Samson had written an abbreviation for Rest In Peace Johann Sebastian Bach.
While I appreciate a good hack as much as the next person, this is not exemplary. If your comment causes confusion instead of dispelling it, remove it.
Rule 5: Explain unidiomatic code in comments
It’s a good idea to comment code that someone else might consider unneeded or redundant, such as this code from App Inventor (the source of all of my positive examples):
final Object value = (new JSONTokener(jsonString)).nextValue();
// Note that JSONTokener.nextValue() may return
// a value equals() to null.
if (value == null || value.equals(null)) {
return null;
}
Without the comment, someone might “simplify” the code or view it as a mysterious but essential incantation. Save future readers time and anxiety by writing down why the code is needed.
A judgment call needs to be made as to whether code requires explanation. When learning Kotlin, I encountered code in an Android tutorial of the form:
if (b == true)
I immediately wondered whether it could be replaced with:
if (b)
as one would do in Java. After a little research, I learned that nullable Boolean variables are explicitly compared to true in order to avoid an ugly null check:
if (b != null && b)
I recommend not including comments for common idioms unless writing a tutorial specifically for novices.
Rule 6: Provide links to the original source of copied code
If you’re like most programmers, you sometimes use code that you find online. Including a reference to the source enables future readers to get the full context, such as:
- what problem was being solved
- who provided the code
- why the solution is recommended
- what commenters thought of it
- whether it still works
- how it can be improved
For example, consider this comment:
/** Converts a Drawable to Bitmap. via https://stackoverflow.com/a/46018816/2219998. */
Following the link to the answer reveals:
- The author of the code is Tomáš Procházka, who is in the top 3% on Stack Overflow.
- One commenter provides an optimization, already incorporated into the repo.
- Another commenter suggests a way to avoid an edge case.
Contrast it with this comment (slightly altered to protect the guilty):
// Magical formula taken from a stackoverflow post, reputedly related to
// human vision perception.
return (int) (0.3 * red + 0.59 * green + 0.11 * blue);
Anyone looking to understand this code is going to have to search for the formula. Pasting in the URL is much quicker than later finding the reference.
Some programmers may be reluctant to indicate that they did not write code themselves, but reusing code can be a smart move, saving time and giving you the benefit of more eyeballs. Of course, you should never paste in code that you don’t understand.
People copy a lot of code from Stack Overflow questions and answers. That code falls under Creative Commons licenses requiring attribution. A reference comment satisfies that requirement.
Similarly, you should reference tutorials that were helpful, so they can be found again and as thanks to their author:
// Many thanks to Chris Veness at http://www.movable-type.co.uk/scripts/latlong.html
// for a great reference and examples.
Rule 7: Include links to external references where they will be most helpful
Of course, not all references are to Stack Overflow. Consider:
// http://tools.ietf.org/html/rfc4180 suggests that CSV lines
// should be terminated by CRLF, hence the \r\n.
csvStringBuilder.append("\r\n");
Links to standards and other documentation can help readers understand the problem your code is solving. While this information may be somewhere in a design document, a well-placed comment gives readers the pointer when and where it is most needed. In this case, following the link indicates that RFC 4180 has been updated by RFC 7111—useful information.
Rule 8: Add comments when fixing bugs
Comments should be added not just when initially writing code but also when modifying it, especially fixing bugs. Consider this comment:
// NOTE: At least in Firefox 2, if the user drags outside of the browser window,
// mouse-move (and even mouse-down) events will not be received until
// the user drags back inside the window. A workaround for this issue
// exists in the implementation for onMouseLeave().
@Override
public void onMouseMove(Widget sender, int x, int y) { .. }
Not only does the comment help the reader understand the code in the current and referenced methods, it is helpful for determining whether the code is still needed and how to test it.
It can also be helpful to reference issue trackers:
// Use the name as the title if the properties did not include one (issue #1425)
While git blame
can be used to find the commit in which a line was added or modified, commit messages tend to be brief, and the most important change (e.g., fixing issue #1425) may not be part of the most recent commit (e.g., moving a method from one file to another).
Rule 9: Use comments to mark incomplete implementations
Sometimes it’s necessary to check in code even though it has known limitations. While it can be tempting not to share known deficiencies in one’s code, it is better to make these explicit, such as with a TODO comment:
// TODO(hal): We are making the decimal separator be a period,
// regardless of the locale of the phone. We need to think about
// how to allow comma as decimal separator, which will require
// updating number parsing and other places that transform numbers
// to strings, such as FormatAsDecimal
Using a standard format for such comments helps with measuring and addressing technical debt. Better yet, add an issue to your tracker, and reference the issue in your comment.
Conclusion
I hope the above examples have shown that comments don’t excuse or fix bad code; they complement good code by providing a different type of information. As Stack Overflow co-founder Jeff Atwood has written, “Code Tells You How, Comments Tell You Why.”
Following these rules should save you and your teammates time and frustration.
That said, I’m sure these rules aren’t exhaustive and look forward to seeing suggested additions in (where else?) the comments.
Memes and cartoons
For your consideration…
Source: https://www.reddit.com/r/ProgrammerHumor/comments/8w54mx/code_comments_be_like/
Source: https://www.reddit.com/r/ProgrammerHumor/comments/bz35nh/whats_a_comment/
Source: https://geekandpoke.typepad.com/geekandpoke/2011/06/code-commenting-made-easy.html
https://geekandpoke.typepad.com/geekandpoke/2008/07/good-comments.html
Source: https://www.commitstrip.com/en/2016/09/01/keep-it-simple-stupid/?
https://www.commitstrip.com/en/2013/07/22/commentaire-de-commit/?
https://geekandpoke.typepad.com/geekandpoke/2009/07/
https://geekandpoke.typepad.com/geekandpoke/2010/11/architecture.html
Tags: comments
77 Comments
I mostly subscribe to the sentiment in this article. I do have one habit that is somewhat different from this set of guidelines: I only link to external sources (e.g. StackOverflow) to attribute the solution or code. I do not want the code to rely on external sources for an explanation. Links rot, need a click and maintenance. So I happily copy from SO, refactor it so it is readable, at the correct abstraction level and style and then add a link to the question/answer.
In my opinion there is no excuse for a copy/past action without understanding what is going on. Even when it works. As the article argues: maintenance and debugging is extremely important and can only be done efficiently when the code is readable.
Todo: fix
“When a hammer is the only tool you have, everything starts to look like a nail.”
A big problem with code comments are that they are frequently the *only* source of documentation, which means people are going to try and account for too many documentation use cases in them. These are great rules and everyone should try to follow them (though personally, I tend to be a *bit* more noisy with comments than prescribed here) – but doing so, you feel like you’re not writing enough to help future programmers quickly onboard to the code.
That’s why it’s import ant to also look at an endeavor-based approach to supplemental documentation, which is specifically designed to convey the background, rationale, and frequent use cases for people to get up to speed, and then they can go spelunking through algorithmic innards as they need to know more. Curl is a really good example – most people grab it because they want to make some kind of HTTP request with crafted headers and get a response – so Curl’s introductory stuff is full of example code that does just that.
But that kind of stuff isn’t as helpful if it’s not made continuous (so your docs evolve with the code), so I really think we’re going to see continuous documentation become a thing (disclaimer: I work for a company that produces doctools, but that’s not the point, the point is a big part of my time goes into helping people get their documentation verified on the CI server)
Micro-service proliferation is something to think about here too, because all the knowledge you need to effectively onboard to a new service really just doesn’t belong packed into a bunch of comments. People need to (like curl) be able to understand what it does, how to talk to it, how to debug it, etc. And this sort of stuff belongs front and center in your service catalogs, not buried in code comments.
tl;dr – There may be plenty of cases where you’ll never need more than a few comments supporting great, readable code and I applaud those who fall into that category. I’m mostly talking about the other 97% that don’t. Comments shouldn’t be the only documentation and as we see these 9 things creating code smells, we should strongly consider that we need different types of docs, and stop trying to cram it all into comments.
This.
Good code comments are very helpful when looking at code through a microscope. But what has frustrated me innumerable times down the years was the lack of ‘mid-level’ documentation that laid out some kind of overview of a significant set of code. I remember encountering an Undo/Redo framework some yeas ago that included high-level documentation on how to use it and adequate code comments ala “step through linked list” but absolutely nothing on how the framework did what it did. It was the kind of code that promoted jokes about Job Security.
(And, of course, if you managed to get ahold of the developer and attempt to ask them questions, you’d get this “can’t you read the code?!” song and dance – if there are documentation tools to help with this, I’d love to hear about them).
One key use case that I think is sort of covered in some of the practices but not explicitly stated is workarounds. I’ve often encountered issues where something doesn’t work the way it’s supposed to due to a bug in a library/framework/etc, and I have to write some code that looks wrong but is necessary. In these cases I think it’s important not to just say “workaround for bug in XYZ”, but to include two key details: the version in use when the bug was found, and a link to the open issue ticket (either one that already exists, or one you’ve opened to report the issue). That way a future dev (or future you) can quickly check to see if the issue is fixed and the dependency can be updated and the workaround removed.
I would have filed that one under “comment when you write unidiomatic code”
The most useful comments tell *why* you are doing what any decent programmer can clearly see you are doing.
This is an important point. The most useful advice I was ever given was that the code should clearly tell you *what* it’s doing, the comments should tell you *why* it’s doing it.
The “comment per line” is appropriate, almost, for Assembler, which does not have the self documenting characteristics of any higher level language. For anything else it is way excessive.
One extra tip. You may find this one controversial or disagreeable, but in my personal experience it has been very helpful. You may not be the same type of person as me, so your mileage may vary.
The tip is: don’t worry about “staying professional” all the time. If something frustrates you, make it known. Hell, write a rant if necessary. Use expletives liberally. As long it gets the meaning across.
So imagine this situation. You just wrote some extremely ugly/confusing code out of necessity. Maybe it makes you cringe too, but after hours of burning your brain out and discussing with your team you came to the conclusion that there is simply no better way to do it. Then I think it’s perfectly reasonable to make your frustration known, so that the potential reader (whether it be future you, your colleague, or your successor) understands that you wrote this shit code out of necessity, not laziness or incompetence.
In other words, feel free to write a rant, so as to advise others to avoid the frustration. Whatever it takes to get the meaning across.
Nobody likes seeing others throwing a tantrum, but it sure beats being misled down a rabbit hole thinking you can do better, only ending up wasting half a day of your life and getting nowhere because everything you thought of has already been tried but they were “too polite” to rant about it.
And finally of course, ranting once in a while does feel good doesn’t it? Programmers are humans after all (although that Copilot thing… It scares me) and we need psychological relief as much as we do physical. It may simply be the most practical thing to do at the moment to regain your composure and become productive again.
I agree with your comment – specially about the rant part.
A problem with using unprofessional speech inside program code is that it may be shown, even accidentally, to managers, clients, or even sensitive co-workers who will take offence at the unprofessionalism. This is more likely with a should-never-happen error message that says “We’re ****ed” during the product demonstration, but it’s also possible that someone will “view source” or try to do a quick edit or debug during a meeting with Very Important People.
You also may be not able to e-mail or download program code that has naughty words in. Your corporate web site update may test fine but not work in the real world because at 5pm you typed “// tits” and went home.
You might get away with using “ROT13” code to hide rude words. There are web sites as well as utilities for this, it consists of swapping the first 13 letters of the alphabet (A to M) with the other 13 (N to Z), and it has the convenient symmetry that tneoyr-ing what you write and un-tneoyr-ing it is the same operation. I’m not sure what best practice is, but I’d ohttre a single word, but vs n jubyr shpxvat fragrapr vf arprffnel gb shpxvat rkcerff ubj shpxvat srq hc v nz jvgu gur jubyr shpxvat shpxrq cebwrpg, that says “probably you don’t want to know”.
If anyone has learned to sight read ROT13, I apologise 😉 I don’t recommend that; its purpose is to hide writing, temporarily, even from yourself. That doesn’t work if you can see it.
The main rule is not included:
Comments should answer “Why?” and “What for?”, not “How?”.
In rare cases “What?” is permissible (e.g. to name an article describing some non-trivial algorithm).
The code blocks on rule 2 broke my eyes. Rule 0 of writing code: use consistent formatting (and colours).
Thanks for reporting this. I’ve requested that it get fixed.
I tend to follow most of these, though I disagree with #8. When fixing a bug, explain the fix in the commit description/explanation, not in a permanent comment in the codebase. Future readers don’t need to know that there *used* to be broken code there, only whether the code there *now* is broken.
Yes, I totally agree with that and in fact have been asking junior programmers to remove such comments or, if they still fall under one of the listed categories above, to rephrase them.
So one time I fixed a bug in a huge project and had a strong suspicion that similar bugs existed elsewhere, so I put in a comment describing the bug, and the fix, and moved on. The team lead deleted the comment (without bothering to tell me, sigh) and when a similar failure turned up elsewhere a few weeks later I went looking for the comment. Couldn’t find it. What should have been a half hour fix became 40 hours of work. I was somewhat irate.
Did you really spend 40 hours fixing a bug because you did not manage to find the commit you authored ?
When I fix a bug, I usually split the work into 2 commits.
The first commit adds a test case that demonstrates the bug.
The second commit fixes the bug and adjusts the test case as necessary, adding the details and background story to the description of the test case.
This approach allows future readers to see the main code only, without any historic or distracting comments. But if they try to “optimize” or “fix” the code, the test case will fail and then the future editor will know why the code is exactly as written. The description around the test case prevents the test case from being thrown away accidentally.
Good and interesting article.
I’m the type who likes to add comments in its code. I agree I might reduce its use, but, bear with me, the projects where I’ve worked and currently working have passed by different hands and most of that code isn’t documented at all, a bug is reported and the only way to review is to read the code and pray that you found the root of the problem.
When I found such issues and I’m able to solved, I let comments about the fix and sometimes, the meaning of the code.
I don’t have much knowledge about code documentation and best practices, but, I think this is an aggregated value to the work (which is already hard).
Keep comments in source code is another way to help in the development cycle.
The rules for good comments may be formulated in a single sentence – “Comment must explain not code, reasoning behind the code”.
I find it very helpful to note the date of changes as part of the comments.
The same comment line (with date) I repeat in the head of the module ordered by date descending.
In this way I have a kind of TOC in every module, showing the last changes.
Most of these rules can be summarized with these two simple lines:
* Code documents HOW
* Comments document WHY
Part of the problem is just bad variable naming. Good variable names remove the need for half of the comments.
The canonical example of “i” is a horrible name. It doesn’t tell you what you are iterating over. Instead a much cleaner code is this example: “for( iCell = 0; iCell < nCell; iCell++ )"
It tells you that you are iterating over cells one cell at a time.
“i” has a huge advantage that it’s idiomatic. You know it’s simply a loop index, at a glance, nothing fishy (unlike something like “iCell”)
And you CAN tell what you are iterating over by the name of whatever collection you use the index to access.
“i” and “j” are so idiomatic that they are clearer for, say, nested loops over rows/cols of a matrix, than “row” and “col”.
True, although the example in this article under “The need for comments could be eliminated with better variable naming” which cites “Node bestNode;” being preferable to “Node n; // best child node candidate” is a terrible example. The question isn’t so much what to name the variable, but by which criteria the “best” candidate is being chosen, and why.
One great use for comments, which has nothing to do with descriptive variable names, is to explain why code changes over time. For example, if the algorithm for choosing the best node changes, the comments should describe that. Even if the code for the old algorithm gets deleted, the comments should remain and describe what it used to do, so that developers can decide whether to resurrect that old code, or to avoid it like the plague.
@Carl-Rainer Zeiss. I agree! This was a “shop rule” standard where I worked back in 1998 and I have used it, and taught it ever since. There is more: comments and comment blocks start with date and also the programmer’s initials. e.g.
Accept and Continue
If the date format is an agreed standard yyyy-MM-dd then we can do code searches like 2021-05- for changes done in May 2021.
Good article! Interesting, unusual and also nice. I had never read anything on this topic before and doing so now that my career has long since come to an end consoles me for the many misunderstandings my efforts have had to encounter and overcome. I’m not a real programmer, but I had to program for a long time and succeeded in all cases. The absence of comments in the programs written by colleagues who preceded me forced me on the one hand to study with great attention and effort what they had done, to understand what the programs should have done and to correct them where it was necessary. In all this type of work I wrote the comments for myself and being a ‘random programmer’ I had to describe very well what the code I found was doing and also the changes I had to make. It will have happened not only to me to struggle in re-reading their comments to the point of succeeding only by studying the code again (written by those who preceded me). This has caused me to get a little ‘picky’ in my comments, but that’s not the same as saying my comments are ‘good’. However, my comments turned out to be ‘quite good’. One of the obstacles I encountered was the lack of foresight of my boss who repeated to me: the programmer who was there before you was much faster to write code (but without even a comment I added) and therefore it cost me less than you . Obviously I replied that he was now paying for what he had not done before me. Then I added that the comments would be useful to my other colleagues and also to those who would take my place in the future. All discussions without a solution, apart from my consistency in continuing to write the comments as I thought necessary.
Years earlier, during my long employment in a large company, I was made to understand that the written code is owned by the company you work for and therefore MUST necessarily be written by someone (or more than one) and then MUST to be read-understood-revised by someone else. So we all understood (some more, some less) the importance of comments, flow charts or representation as a state machine.
Returning now to the company where I was, I heard the same rule repeated and it was not as difficult for me as it was for others to adapt to it. I had colleagues who did not write a single line of comments.
One day I myself took over who would later take my place. A smart young man who later made an enviable career. We got along very well and then I said to him: “If you don’t understand something in the bad code I wrote, please call me and I will help you solve the problems that I have certainly left all over the place.” He agreed, but for over a year he never asked me anything. This made me think that he was a proud guy and that he didn’t want to admit that he didn’t understand the code written by another. But after all that time one day I asked him why he had never asked me for help. The answer it’s been that he often wanted to ask me for explanations, but by reading my long comments on the side of the code he had clearly understood and had always managed to find the answers to the doubts about it. This for me it’s been the greatest gratification of my efforts.
Rule 0: Be kind to your future self.
To me, the most valuable information about a given piece of code is data semantics. This seems to be completely ignored by most programmers. Without knowing what the data means, the operations are irrelevant. There are lots of ways to make semantics manifest, but there doesn’t seem to nearly enough adoption, and too often, you simply can’t use something like an enumeration in a given context.
You left out the most important rule (#10?):
Never write a comment until you have to.
If you write good code, then comments should be extremely infrequent. I’ve written whole code bases without a single comment, and they are perfectly readable. Naming, single responsibility, small functions, small files, clear hierarchy, etc. will get you 99% of the way there.
Comments are best to explain necessary but regrettable hacks (#5) and, maybe, to indicate incomplete implementations and reasons why (though a README is probably sufficient) (#9).
Exactly.
I often say that comments are typically either “useless” or “dangerous”.
I only use them to explain something strange/missing.
For example, an empty exception handler. I’ll put a comment “// ignore error” in the body just so I won’t spend time wondering why its empty.
Clear and precise names, objects, and structure makes for quality AND readable code.
Crazy sounding I know – but neither I nor my colleagues have trouble reading the code.
“// ignore error ” is a bad comment, since it’s easy to see you’ve ignored the error. Better to explain why you can ignore the error.
Exactly, this should be rule #0. If you follow rules 1-4 comments really shouldn’t be needed. I only use comments for unidiomatic code (rule 5), copied code (rule 6), and todos (rule 9, also I complete or remove all todos before publishing a PR so really noone else would see them). For external references and bugs (rule 7 and 8) I rely on the version control and task management tool I am using (e.g. GIT and Azure Devops).
I found it useful to write the comments before the code. Describing the flow in plain English often sorted out my thinking about the coding. Then again, I’m self-taught.
If a developer is unable to express themselves clearly in code, what makes you think that they would be able to do a better job in comments?
I avoid comments as much as possible apart from leaving links to copied code.
E.g. I think there are better ways to avoid deletion of code than writing a comment that the code is needed… Write a test instead that shows why the code is needed. It’s a so much better way to deal with that issue.
Tests are the correct answer to a lot of the use cases for comments in this article. Read again and see for yourself.
The best comment I have ever seen?
stop(); // Hammertime!
I have only one rule for comments. Only use them to express information which must be retained and absolutely cannot be conveyed using the programming language itself. You can get a hell of a long way with a meaningful naming regime in your code.
I have three.
https://ruthlesslyhelpful.net/2012/02/25/rules-for-commenting-code/
They’ve been working really well for me for about 20 years now.
There are different levels of expertise that manifest in programmers, I would think that if you are not writing a production program, commenting it to the maximum would be the best for the lower-level experts to understand and learn the tricks.
It can be a turn off for a code that has so many comments, but if they are not confusing, I think it should be tolerable.
Quibble with Rule 2: I would not trust a variable name to describe the contents of the variable. (Or a constant.) I would not try to name a variable comprehensively either. If need be, use comments to say what is really going on. An expressive name is good, but a long name is not so good for several reasons; you can mistype it, or you can confuse it with other similar variable names. And if a variable only needs to exist for five lines, by all means call it n. Or tmp. Or tmp_1750.
How can you mistype a variable name without the compiler immediately recognizing it?
Quibble with Rule 3 – separate comment because I’m not seeing paragraphs here. A neat twist in program code can be legitimate and also can take a disproportionate time to explain in comments to uninitiated colleagues. If you’re relying on the unique prime factorisation of integers, or on Fermat’s Last Theorem, … leave off at a hint. 😉 However, I will shortly make an opposite argument regarding Rule 5…
If the code and comments don’t match, then you can’t trust either.
If I’m on a bug hunt than the place they don’t match is often where the bug is found.
Great article. I am glad someone takes a stand against the non-believers. For over 50 years, comments have helped me inherit code written by newbies and people who ‘havent had the time’ to descrive their work only to lead to onger fix and enhancement times down the road. Programs are like books. They tell a story. We all know how a foreword, table of contents, chapter headings, indexes and bibliographies can help add to our knowledge of certain topics. Why not in a program as well? Remove the code from a well commented program and the story appears. Descriptive variable names cannot do comments any justice. I look forward to any addendums.
Too me the most important comment is WHY, to document Business Logic. It could be a link to come ticketing system is a comment on who requested the rule implemented.
// Requested by Hakon Hansson, Norwegian store manager on slack 2019-03-15
If (store == “no” and year > now().year().plus(20, YEAR) then year = now().year().plus(20, YEAR)
TODO is useless as it will never be implemented, believe me.
It seems to depend on whether you are in control of the code, or just following orders.
We regularly sweep for TODO activities and resolve them.
If you have to have permission from an authority (or a Jira system) to do that, then you probably will never get the chance.
Rule 7: add a link. More than once I got a 404, even with code I wouldn’t call “old”. So some semantic context/keywords (i.e. the RFC code) needs to be added, for sure not a bit.ly shortcut.
In all of my years of programming, my best use of comments outlines three particular things:
– Who (made the code or change)
– When (was it made) and
– Why.
The last part (Why) has been incredibly useful when the boss asks that code be changed to meet a specific criteria, and when reviewing the code, I find a comment that said that I made a change 3 years earlier that explicitly excluded the criteria, at the direct request of the boss. That enabled a different discussion which would not have happened had the code not been there, and it would have been very unlikely that Git Blame would have been checked, because who remembers code changes from 3 years ago?
Slight disagreement on rule 1, or at least the example. As a programmer in the C family of languages I often find myself with multiply nested blocks, and it can be really handy to know which block the closing brace is closing. Am I closing while(x>1) or if(y<0)? Also nice to know which method a closing brace is ending.
For me, the code may say what it does, but the comment says what it is supposed to do, or at least what I thought it would do.
“For me, the code may say what it does, but the comment says what it is supposed to do, or at least what I thought it would do.”
Yes, the comment states your intention. Perhaps the code has a bug in it, but at least someone who looks at it later knows what your were trying to do and can fix the code accordingly.
Quibble with Rule 5 – actually with the examples. I’m in favour of commenting basic statements when you are working with a “foreign language”, either for yourself or for co-workers. You and that probably will need the reminder. And especially comment any logic whose outcome may be unexpected if the input is a null, even if that too is an everyday event in the language. You could still overlook it.
One reason I have a lot of comments in my code is that I tend to outline the steps of a process I’m going to code with comments first, then I plug in the code details later. I usually just leave those comments in the code because it provides a “big picture” view of a process which is helpful for anyone reading the code later.
So, those types of comments are basically describing WHAT a chunk of code does. I find them useful when trying to find certain functionality in the code later. Without those comments, trying to find something in the code involves reading a bunch of lines of code, deciphering what the code is doing, and then realizing that is not the code you are looking for. Repeat until you finally find the code you are looking for. A simple comment describing WHAT a block of code does would prevent a lot of wasted time and effort.
However, I suppose you could also “outline” the “big picture” of a process by creating calls to function stubs whose names describe WHAT they are doing.
Then you can plug in the code details into those function stubs later.
In a half-century career as a programmer I have probably tended to overdo comments. One thing that influenced me early on was a statement made by a colleague, Dave Shields, that, “The most powerful statement in any programming language is the comment”. Another was coming across a couple of huge codebases that were almost devoid of commentary and a nightmare for me to work with.
I started out with some bad habits but over the years have become aligned very much with the rules laid out here. There is however one aspect of commenting that these rules do little to address and that is the scope of comments. While I would say that commenting every statement or even every block is overkill and ill advised, I think that commentary at the function/procedure level should be mandatory. The name of a variable or constant can say a lot about its purpose but the name of a function often cannot without making it unwieldy. I make it a rule to include at least one comment at the top of every function. This comment not only summarizes the function’s purpose but also describes its context. By context, I do not mean such things as stating where it is called from — that would be unwise — but might, for example, note that it is called only in debug builds or that there is, intentionally, another very similar function elsewhere. I also make it a rule to have some commentary at the module level. This is to summarize what the module contains and again give it some context.
A practice that I took up only very late in my career is to include lots of assertions in my code. Assertions are powerful because, if used well, they serve equally as code and as commentary.
Interesting blog, comments indeed need to be added professionally to the code and should be kept well separated from the code as it could create several bugs and leave the programmer scratching their head while debugging. The most important tips for me while adding comments is that the comments should be precise as I have seen a lot web app developers writing long lines and sometimes even paragraphs as comment when it could be much more precise.
nice blog
// Conveys the deeper meaning of life while making sure that garbage collection has enough time to run
while(true){
This was a well written piece in an otherwise busy space of comment blogs. I didn’t expect to enjoy it as much as I did. Good tip for the referenced tutorials, I think I don’t do that enough, and probably should start.
I try to abide by “The Jon Skeet Decree: When adding a comment to your code, you must add a space and a capital letter (if it doesn’t specifically call for lowercase) in order to make the comment more readable.” (from https://stackoverflow.blog/2018/03/14/podcast-123-jon-skeet-wants-you-to-be-a-feminist/).
}
While I love, love, love rules two and three…the “Cat” cartoon is the best. Thanks.
Too complex/time consuming. Just need 2 simple rules:
1) Keep your methods small enough so that you can 100% automated unit test coverage for each (and write those unit tests!).
2) Add documentation for each method saying what it does, return vals, params, etc).
If you find yourself having to add comments INSIDE a method, your method is too complex and you need to see rule 1 again.
What is about commenting out code?
Believe it or not, there are situations where there is no source control, usually ‘legacy’ systems. In these situations, comments have a higher value, including commenting out unused code or showing any changes that have been made. After some reasonable time, these old lines should be removed for clarity.
A decent source control system is obviously, preferred.
Also, if you need to comment out code, make sure that it is obviously commented out to prevent any ambiguity. (Legacy systems often don’t have a modern IDE to colour code comments, and if they do, that may not help the 7-10% of males that are colour blind.)
You said everything so nicely and all of them are helpful. Thanks.
Ironically, instead of this code:
final Object value = (new JSONTokener(jsonString)).nextValue();
// Note that JSONTokener.nextValue() may return
// a value equals() to null.
if (value == null || value.equals(null)) {
return null;
}
This would be much cleaner and intention-revealing:
final Object value = (new JSONTokener(jsonString)).nextValue();
if (value == null || value == JSONObject.NULL) {
return null;
}
No comment needed. JSONObject.NULL is the only thing returned by nextValue() that causes value.equals(null) to be true, is a singleton/constant (thus == used) and is pretty much self explanatory, or at least easy to check its doc comment of with any IDE, for clarification.
Also, in addition to the good rules here, one super important rule that is missed, is that extracting methods can remove needs for comments AND add all other extra benefits of abstraction, modularization and simpler context.
Yup, your suggestion better clarifies the intent here. That code in Rule 5 blew my mind: how could an object be equal to null but not be null itself?
It just so happens that nextValue() could return a JSONObject, JSONArray, String, Boolean, Integer, Long, Double, or JSONObject.NULL.
https://docs.spring.io/spring-boot/docs/2.3.0.M4/api//org/springframework/boot/configurationprocessor/json/JSONTokener.html#nextValue–
Oddly enough, JSONObject.NULL “violates the general contract of Object.equals(java.lang.Object) by returning true when compared to null.” https://docs.spring.io/spring-boot/docs/2.3.0.M4/api//org/springframework/boot/configurationprocessor/json/JSONObject.html#NULL
Excellent points! I didn’t think through what value could be equals() to null, and I agree with you about extracting methods.
You should consider writing for the Overflow. You have lots of good ideas and write well.
Great article… I love it…
1. I once worked somewhere where the Software Manager told the developers to never comment the code because when the code changes, the comments don’t, and the code becomes harder to maintain. Fortunately, I replaced him, and my first official duty was to explain to the team that comments were necessary, and how to write good comments. Over the decades, I have increasingly encountered cultures of developers who do not, will not, comment code.
2. Sometimes I write the comment before I write the code. This is sort of like Test Driven Development, because the comment becomes a specification,sort of a test, and a reminder to maybe write an automated test too. At a minimum, the comment reminds me to do manual testing to validate/verify the claims of the comment.
3. Often I name variables ‘foo’, foobar’, ‘foo1’, etc. when I am rapidly prototyping. I used these name to remind myself (an implicit TODO comment) they are ridiculous, and I need to rename them later, after I finally understand the code I have written and tested. I really wish modern compilers could give warnings for ‘ridiculous’ names, and it would not be hard to agree on a canonical set of ridiculous names.
4. When I am debugging, I always leave the log statements in the code, but set to a lower level such as ‘debug’ or ‘trace’, because they are in effect comments of investigation and problem resolution. I keep running into people who will not pass my code review requests until I remove the debug and trace log statements.
Thank you!
A great post without any doubt.
Please mention “comments before assertions in test code” anti-pattern.
The anti-pattern I often see is
“`
// microseconds part is less than 500, so should round down
assertEquals(“08:20:40.000”, roundTimeToMillis(“08:20:40.000499”))
“`
Even though the comment explains why 000 is expected, it won’t help for those who observe test failure.
The failure might look like “assertionError: expected 08:20:40.000 got 08:20:40.001”.
A much better option is to move the comment into the assertion comment:
“`
assertEquals(“08:20:40.000”, roundTimeToMillis(“08:20:40.000499”), “microseconds part in 08:20:40.000499 is less than 500, so should round down”)
“`
Then the failing test would print the reason for the expectation, and it would be much easier to understand the reason for the expectation.
The remaining part (Why) has been especially beneficial whilst the boss asks that code be modified to satisfy selected criteria, and whilst reviewing the code, I discover a remark that stated that I made an extra three years in advance that explicitly excluded the criteria, on the direct request of the boss. That enabled a distinct dialogue that might now no longer have come about had the code now no longer been there, and it might have been most unlikely that Git Blame might have been checked, due to the fact who recollects code modifications from three years ago?
Rule 5> With respect to `if (b == true)` versus `if (b)`, isn’t the difference due to the data type of `b`? The former only if `b` is `nullable bool; the latter is possible only if `b` is bool. That is, use whatever expression is appropriate, and specifically don’t write `b == true` if `b` is `bool`.
Wow, this blog post is a lifesaver for me! As a software developer, I always struggle with finding the right balance between writing too many comments and not enough. This post has really helped me understand the importance of writing clear and concise comments that add value to the code. I especially appreciated the tips on avoiding redundancy and using comments to document complex logic. Thank you for sharing these valuable insights!
There are a few different schools of thought when it comes to writing code comments. Some people believe that comments should be brief and to the point, while others believe that comments should be thorough and provide as much detail as possible.Personally, I believe that comments should be both clear and concise. This means that they should provide enough information to understand what the code is doing, without being so verbose that they become difficult to read.What do you think are the best practices for writing code comments? Let us know in the comments below!
Wow, this blog post is a lifesaver for me! As a software developer, I always struggle with finding the right balance between writing too many comments and not enough.