The author touches on this a bit but I want to state this really simply:
Codes needs "why" comments, not "what" comments. The "what" can be done by self-documenting code, _most_ of the time. You still need to write "what" comments sometimes, don't rule it out completely. And you routinely need to write "why" comments, self-documenting code will never provide the context of "why".
This is the thing that annoys me with arguments about all this. I will generally say "document the 'why' and not the 'what'; if the 'what' isn't clear from the code, fix the code to make it clear". And people will say "that's invalid because sometimes you really do need to do something clever and the 'what' needs to be documented" or whatever. And yes, that's fine! Stop taking everything people say as if it's an absolute, 100%-of-the-time statement! Obviously there are exceptions. That doesn't make the common-case rule useless or uninteresting. People should assume "most of the time" is the default implication about these sorts of "rules" unless otherwise noted.
I think people often lean too far in favour of "self-documenting code," and "what" comments wind up being underused. The idea of self-documenting code is that the documentation is encoded in the function and variable names, more or less. Sure, extraneous comments should usually be avoided, but frankly, sometimes a couple sentences of plain English is just clearer. I'd rather people just tell me what's going on than try to squeeze it in to a few camel-case words if that would compromise the clarity.
It's much easier to fool yourself into thinking you're writing "self-documenting code" if your "documentation" is just a natural language rephrasing of your code.
When your coding standard enforces 80 characters, you end with a single logical statement split across many lines. This quickly approaches un-readability if you fetishize self documenting code. In my opinion, short variable and function names with a comments are often more readable that self documenting behemoths, e.g.,
What about the day the Mars climate orbiter was lost because one piece of code wrongly assumed that the units of figures out of the other piece of code? :)
I find that you do sometimes need symbol names that long even though it's a code smell indicating something else is up - usually scopes that are too large or insufficiently expressive types.
I view excessively long variable names as a midpoint on a journey to improving code quality on really bad code bases.
While what you say about long variable names can be right in certain circumstances, the example with the wrong units is inappropriate.
In my opinion any code that deals with physical quantities, but which does not use dedicated types for each kind of physical quantity, where the type definitions include the units used, is erroneous.
Any mistake like in the Mars orbiter must be detected at compile time.
* It's not your code - I was working with some UNIX TTY code recently and the APIs are horrendous 1970s throwbacks. But they're not changing. Working with crappy APIs is where comments about the "what" shine the most. You can only go so far with abstraction/encapsulation/facades.
* You don't have time - there's an urgent bugfix you need to get out yesterday. Do you A) rewrite the architecture to make it perfectly understandable and tell the users to wait B) drop a comment in explaining what's going on.
> And people will say "that's invalid because sometimes you really do need to do something clever and the 'what' needs to be documented" or whatever.
Yep, that'll happen, and when it does, you document why you documented the 'what'! :P
This kind of lazy black-and-white thinking is what 'consistency is the hobgoblin of small minds' is talking about. The exception proves the rule (...most of the time...)
Nicely said. One small adjustment I'd suggest -- because I care a lot about setting incentives -- is that we generally want not just _more_ "why" comments, but in particular concise "why" comments that empathize with and improve the life of future developers. They're a kind of message-in-a-bottle conversation.
The point on API methods is incredibly important. True, "Why" is the most important kind of comment, but I disagree with the author's critique of the documentation-generating comments and agree with you.
Yes, "what" comments are a source of additional developer effort and of possible inconsistencies between code and comments, but good API documentation is worth writing and maintaining.
No matter how well you name your method and its arguments, if it's part of the public API and you don't include a comprehensive documentation comment, I'll be forced to dig through your source.
With a public API it's important to distinguish "currently true from reading the code" and "promised to stay true through minor updates". What comments are useful for that
If a method is part of an API that might be used by developers external to your team, they don’t want to be looking through your code to figure out how to use your methods.
I mostly agree with you, but there are certain cases where you’re making weird and non-intuitive performance optimizations and have to explain “the what”.
But still you have to explain why your code is the way it is on top of that. You can always deduct the what from mentally processing the code, but you can't deduct the why, you only guess.
Definitely agree there are still cases where "what" is useful. Especially in some code that I come across that I just can't grok (e.g. badly named things), I start writing what it does just as a rubber duck method, and I don't have capacity to refactor the whole thing right then, at least I will leave behind a "what" comment because if I had trouble grokking what it does then someone else likely will too.
Wanna jump in here and say I'm working on this exact problem using GPT-3. The results when added to other dev tools have been really inspiring, and I'm learning how flexible comments can be when addressing the "why"[1] instead of just the "how".
Absolutely. I worked a codebase that was littered with ticket numbers for a few years; many members of the team religiously added them but never read them. How do we know? Self hosted issue system and access logs. New starts would read a couple and that was it.
A ticket is a really opaque way of showing something. If you click the issue you're now going through several tangentially related comments and a few MR back and forths just to work out if its even useful to you. People will stop bothering to even open the link unless they are really stuck.
Whack a summary comment and then by all means include the link, but the comment is what most people will use.
Yes. Unless your explanation is thousands of words (which it probably shouldn't be), just inline it. I only speak for me, but if I had to look up (potentially multiple) Jira tickets just to understand what was happening in a source file, I wouldn't be at that job very long.
That depends on whether or not you think the Jira ticket will outlast your code or not. If the company will definitely never replace Jira then no. If there is a chance that they will, and for your sake I really hope there is, then you'll need a better comment.
I find it's often useful to put a 'what commment' on a block of a few (2-10) lines of code. Then you can skim through the function without reading every individual line of code.
Please no. Don't extract methods methods just to avoid using a comment. You end up making code more difficult to follow when you do that since you're now jumping somewhere else in the file and you're adding a lot of noise by having to pass the function's current state along as arguments to the new function.
>by having to pass the function's current state along as arguments to the new function.
But then the state being read and mutated by a block of code is explicit. The alternative is having to inspect the code to determine if and what state is being manipulated. The more code in a functional unit, the harder this gets.
If you're mutating variables then I'd agree that it might be best to encapsulate that in a function. But I'd argue that except for performance sensitive code, that's bad practice. The vast majority of code I write has 95% immutable variables. So even in a large function they'll typically be at most one or two mutable variables to keep track of (and very often zero).
It depends, if it needed a comment in the first place it likely was at a different (likely lower) level of abstraction than the rest of the code in that method.
Yes, but that doesn't mean it doesn't benefit from being inline. I love being able to skim over the paragraph-comments to get the big picture and then dip into the details without having to jump to another file / part of the file and lose my context.
To me it's actually faster and easier to read code instead. This is because i generally don't trust "what" comments - either I know the code already or I don't, and if I don't, then I don't trust the "what" comment fully, so I both have to parse "what" and code.
A "why" comment usually ages well, so I generally trust it and can use it to better parse the code and its intentions.
So I belong in the "what only for API methods" camp.
This is Damian Conway's "coding in paragraphs" advice, and in my experience, it's the single best advice for more readable code. When you have a large function, split the code into blocks with a heading comment on each section. That way, you can skim through the function body quickly by just reading the headings, then dive deeper into whatever block you're interested in. Like section headings on an article.
Yes, I've also done this in places. It works pretty well, although I wish I could get my syntax highlighter to de-emphasise these lines like it does with comments.
Ignoring the pedantry elsewhere, then sure this comment explains what you are doing.
The contention is whether it is necessary or common enough to be assumed. In most places this would be self evident and should be avoided to reduce mental load, however I have seen and done exactly this many times - in distrubuted or embedded systems where the control is neither synchronous or co-located (ie, coming in from interrupts / messages). In such places there may even be call for more description, however presuming the context is appropriate this could be correct.
I would imagine a game with a react redux style state engine might result in a comment like this as well.
it feels like this suggests a design flaw. You should probably be creating a new local `total_count` variable for each run that goes out of scope at the end of the run, rather than reusing the same variable over and over again in different contexts
sure, but reusing an integer variable is never going to bring a 2% speedup. allocating a new variable of almost any type is not going to cause any performance issues, at least if it is allocated on the stack, and if the construction of the variable is particularly expensive, resetting it probably will be too.
I would need to see a realistic example to believe that something like this would ever be a useful comment.
In a compiled language, this is true for a local variable in general, it costs nothing because the compiler uses a register for it (or at worst spill it on stack but the stack frame size is reserved once for all stack allocated variables when the function is called).
The story is different, if the variable is captured by a closure/lambda, the current stack frame or part of it may be allocated on heap, leading to perf issues.
> the stack frame size is reserved once for all stack allocated variables when the function is called
Is there somewhere that's documented for LLVM? That's my expectation of what the final compiler output should be, but I read that the IR allocates every variable dynamically, and then they optimize away whatever they can. I haven't been able to figure out how or where it's guaranteed that all allocations will be coalesced.
I wouldn't normally worry about it, but I've talked to folks who don't believe me when I say it doesn't matter whether you declare an integer inside or outside of a loop. It would be nice to be able to explain exactly why it can never matter.
I don't know LLVM well enough, but you can play with godbolt
By example, with
https://godbolt.org/z/9Kv7oo9oK
you can see that values goes into registers (and that thank to 'lea' there is not many registers used).
And if you remove the option -O2, values are spilled on stack.
For the next run. Why is in the comment, it's likely a design flaw though and no comment is going to help here. See comment about using proper scoping.
Codes needs "why" comments, not "what" comments. The "what" can be done by self-documenting code, _most_ of the time. You still need to write "what" comments sometimes, don't rule it out completely. And you routinely need to write "why" comments, self-documenting code will never provide the context of "why".
Write more "why" comments.