Comment by sudobash1
9 months ago
There is an important case for comments that neither of them touched on. Sometimes you are dealing with bugs or counterintuitive processes beyond your control.
For example, I am writing some driver software for a USB device right now. It is so easy to get the device into a bad state, even when staying within the documented protocol. Every time I implement a workaround, or figure out exactly how the device expects a message to appear, I put in a comment to document it. Otherwise, when (inevitably) the code needs to have features added, or refactoring, I will completely forget why I wrote it that way.
The prime number example is a self-contained, deterministic algorithm. While I did find it far easier to parse with comments, I could still spend the time to understand it without them. In my USB device driver, no amount of review without comments would tell another person why I wrote the sequence of commands a certain way, or what timings are important.
The only way around that would be with stupid method names like `requestSerialNumberButDontCallThisAfterSettingDisplayData` or `sendDisplayDataButDontCallTwiceWithin100Ms`.
> The only way around that would be with stupid method names
Yep. Method names make terrible comments. No spaces, hard to visually parse, and that's before acronyms and ambiguity enter the conversation.
As the person who often writes borderline-essay-length comment blocks explaining particularly spooky behaviors or things to keep in mind when dealing with a piece of counterintuitive/sensitive/scary code, my reason for mega-commenting is even simpler: all the stuff I put in comments should absolutely instead live in adjacent documentation (or ADRs, troubleshooting logs, runbooks, etc). When I put it in those places, people do not read it, and then they do the wrong things with the code. When I put it in comments, they read it, as evidenced by the rate of "that bug caused by updating the scary code in the wrong way happened again"-type events dropping to zero. It's easier to fix comment blocks than it is to fix engineers.
Upvote for mentioning ADRs! ;)
I really like the approach and as other comments already mentioned, this is a nice way to capture the "why" of specific decisions that go beyond the "how" in the code.
Here is a good starting point for people not familiar with ADRs: - https://cognitect.com/blog/2011/11/15/documenting-architectu... - https://adr.github.io/
> Yep. Method names make terrible comments. No spaces, hard to visually parse, and that's before acronyms and ambiguity enter the conversation.
Which is why snake_case or kebab-case (if the language allows it) is much better than PascalCase or camelCase.
Even worse when camelCase enters into JSON because people want to automate the serde but are too lazy to make the actual interface (the JSON Schema) easy to read and debug.
> I will completely forget why I wrote it that way.
This is the main reason for comments. The code can never tell you "why".
Code is inherently about "what" and "how". The "why" must be expressed in prose.
And the described use case - USB stuff with very specific exception - makes a strong case for literate programming, that is, more prose than code.
Does everything have to be pushed into a structure-prescribing set of rules?
Can't we just say "comments are useful here" without trying to make it into a case for $methodology?
1 reply →
Why not put the prose in the name of the function?
Function names are limited. E.g. can't provide a circuit diagram of what you're controlling in a function name. But you can do that in a comment (either with ASCII art or an image link).
3 replies →
> For example, I am writing some driver software for a USB device right now. It is so easy to get the device into a bad state, even when staying within the documented protocol. Every time I implement a workaround, or figure out exactly how the device expects a message to appear, I put in a comment to document it. Otherwise, when (inevitably) the code needs to have features added, or refactoring, I will completely forget why I wrote it that way.
I believe in general there is a case for this (your case sounds like a perfect candidate). The implementation of Dtrace is another example[0] full of good description, including ASCII diagrams (aside: a case for knowing a bit of Emacs (though I'm sure vim has diagramming too, which I would know if I pulled myself out of nvi long enough to find out)).
[0] https://github.com/opendtrace/opendtrace/blob/master/lib/lib...
While I am not a Uncle Bob-style "no comments"er I do love a ridiculous method name. I pay very close attention to that method and the context in which it is called because, well, it must be doing something very weird to deserve a name length like that.
That’s exactly why you should save that length only for a method that’s indeed doing something weird. If every method is long, the codebase turns into noise. (IOW I agree)
This doesn’t happen in reality. Your program does so many things that practically speaking short names work for a lot of functions in the program. It’s like English. There are big words and there are small words and usually to communicate a combination of big and small words are used.
Nobody practically communicates with big words. A long function name only pops up when needed.
3 replies →
I used to work this way, but I found that every non-trivial method involves edge-cases and workarounds documenting them the method name destroyed readability.
1 reply →
There are a few Haskell functions with names like reallyUnsafePtrEquality# or accursedUnutterablePerformIO, and you know something interesting is going on :P
Sounds like you should instead be making these invalid states unrepresentable by encoding them in types and/or adding assertions. Especially if you're exposing them as interfaces, as your example function names would imply.
They're invalid states inside the USB device, not inside the driver code. So nothing you do to the driver code can make them unrepresentable. The best you can do is avoid frobbing the device in the problematic ways.
The GP is making those invalid states unreachable by writing a device driver.
I don’t see anything wrong with those names. A bit hard to parse but the name moves with the function call while a comment does not.
It’s annoying to look at but when you actually read the function you know what it does. A more elegantly named function is less annoying to read but less informative and doesn’t provide critical information.
The name just looks ugly. But it’s like people have this ocd need to make things elegant when elegance is actually detrimental to the user. Can you actually give a legitimate reason why a method name like that is stupid other then its “hard to parse”. Like another user said… use snake case if you want to make it easier.
Encoding temporal dependencies (or exclusions) between methods is hard. You can get partially there by using something like a typestate pattern (common in rust).
Have you thought about distilling your hard-earned information about the device's behavior into a simulator for the device you could test your code against?