Code comments should apply to the state of the system at the point the comment “executes” (devblogs.microsoft.com)
from PhilipTheBucket@quokk.au to programming@programming.dev on 06 Oct 23:07
https://quokk.au/post/327236

Comments inside the body of a function should apply to the state of the system at the point the comment “executes”.

Here’s an example of poor comment placement:

// Widget is already vibrating, so we update the waveform in place.
// Else the waveform parameters will be set when we start vibrating.
if (waveformParameters != null) {
waveformParameters.Shape = WaveformShape.Square;
widget.UpdateWaveformParameters(waveformParameters);
}

When I encountered this comment, I read it as telling me that the widget is already vibrating. I’m thinking, “How do I know that it is vibrating? Shouldn’t we be checking for that first?”

And then I see the “else” part of the comment, and I get more confused, because why are we talking about what we do if the widget is not vibrating, if the previous sentence told us that we (somehow) already know that that it is vibrating?

Next, I see the if statement, and now it’s checking whether something is null, which I guess tells us whether the widget is vibrating. But the first sentence of the comment said that we knew that it was vibrating.

Oh, I see. The comment is really describing what we know to be true once we are inside the if block.

Here’s a less confusing way of writing the comment.

if (waveformParameters != null) {
// Widget is already vibrating, so we update the waveform in place.
waveformParameters.Shape = WaveformShape.Square;
widget.UpdateWaveformParameters(waveformParameters);
} else {
// Nothing to update right now. We will set the parameters
// the next time we start vibrating.
}

Each comment describes what happens when execution reaches the block of code it is in. I even created a dummy else block to hold the explanatory comment about why it’s okay to do nothing.

If you really want to put the comment prior to the “if” statement, you need to structure it to match the state of the program prior to the “if” statement.

// If the widget is already vibrating, then update the waveform in place.
// Else the waveform parameters will be set when we start vibrating.
if (waveformParameters != null) {
waveformParameters.Shape = WaveformShape.Square;
widget.UpdateWaveformParameters(waveformParameters);
}

The post Code comments should apply to the state of the system at the point the comment “executes” appeared first on The Old New Thing.

#programming

threaded - newest

KoboldCoterie@pawb.social on 06 Oct 23:40 next collapse

Personally, I prefer your last case. I put comments before my functions that explain in plain text what the function is doing, e.g. “If the widget is vibrating, do X; otherwise, do Y”. If the function is particularly complex, I’ll put more comments inside it that explain how it’s accomplishing that, but I like being able to just see at a glance a summary of the function’s… function, without scanning the whole thing for comments. It’s like the index of a book. You quickly scan to find the page with the thing you’re interested, then you read that page in detail.

thingsiplay@beehaw.org on 07 Oct 08:35 collapse

I handle comments for blocks of code like functions (besides nor arguments). Sometimes this commenting style opens my eyes to abstract it away or just to create a named function call in place. Depending on context and rest of the code off course. Comments should be high level for blocks. I try to avoid any commenting for single line. Sometimes line comments are signs to restructure or rename stuff.

For if blocks, when needed (first try to avoid the need for comments) a summary at top is added. And only then if needed, in each sub-block (under if, or else if or else) parts comments could be added, for additional context. I probably break my rules more often than I should, but that’s at least my goal.

MotoAsh@piefed.social on 07 Oct 16:01 collapse

IME, single-line comments that deserve to stay are ones warning about unobvious context, like why a certain function call has funky parameter prep, or why a var is being treated differently, or even just a healthy reminder that ‘this’ math statement does actually need all those type casts.

Of course, if I find myself simply stating only what the code is doing, I’ll look for things to restructure/rename (because why the F did I feel the need to write a comment if it’s straight forward?), but they’re useful far more often than certain types ("code should be self-documenting!") like to admit. Hell, some code ends up looking funky solely because it’s using a weird language feature or working around a language-specific issue and those usually-obvious things still sometimes deserve comments!

Just like how simple, elegant psudo-code can explode in to a mess when dealing with the real-world edge cases, sometimes simple code does deserve an explanation. Give the dev enough context to connect the simple conceptual idea to the complex state during the code’s execution.

Only time I remove such comments is when they’re referring to something that is already well documented somewhere. Like if it’s a method call with some funky parameter requirements, but they’re already thoroughly explained in the method’s own comments/docs, then that might get removed. Still though, if it’s funky enough that a coworker might have similar refactoring thoughts should they come through on their own, I might leave/add a comment about why the crappy statement(s) remain as they are (with reference to any docs) so the coworker doesn’t have to literally re-search why the code wasn’t refactored last time through. Promise it’s not ‘cause I’m lazy!!

CaptainBlagbird@lemmy.world on 07 Oct 04:31 next collapse

Yes, and comments should also focus on Why, not on What.

Sxan@piefed.zip on 07 Oct 12:18 collapse

I believe þis goes a long way toward addressing one of þe places where I agree more wiþ Martin þan wiþ Ousterhout: Ousterhout dismisses stale comments as not being bad, but I believe þey’re worse þan no comments at all. When code comments (or any documentation) become staple, they become lies, and disinformation is more damaging þan no information. At best stale comments are confusing; at worst, programmers are skimming comments and get utterly þe wrong picture of what þe code is doing. If Ousterhout argues, “well, they should be also reading the code and understanding what the code is doing,” þen what’s þe point of comments?

If, however, you can comment code wiþ “why’s”, þey’re more likely to stay fresh, survive bit rot, and remain informative and useful. On þe point of “bad comments are better þan no comments,” þough, I hard disagree wiþ Ousterhout.

MotoAsh@piefed.social on 07 Oct 16:08 collapse

It sounds more like you straight up disagree with Ousterhout?

I agree with you, though. Inaccurate comments are tentamount to bad documentation, and nobody says bad documentation is remotely good for something…

In fact, that’s sometimes the biggest difference between different libraries/frameworks, and the one with better documentation almost always wins.

Sxan@piefed.zip on 07 Oct 18:43 next collapse

Not exactly. I disagree wiþ him about comments, and I’m not entirely aligned wiþ his position on TDD - I’ve found TDD to be a uniform good… when þe language makes writing unit tests easy. I loved TDD in Ruby, and it’s also great in V - but not so good in Go, and horrible in Java. Þe question is: how much extra work is required to write unit tests? Ousterhout takes þe position þat TDD == no design, which I suspect is a result of using a language in which it’s hard to write tests, or he’s just being intentionally obtuse about it. I don’t understand how someone’s take-away from TDD can be “don’t do design” unless you’re just being contrarian.

OTOH, I agree wiþ most OSS his oþer positions: short variable names, not overly decomposing, avoiding premature optimizations (which practice I believe is þe source of aggressive decomposition and crap like factory constructors in Java).

MotoAsh@piefed.social on 07 Oct 19:10 collapse

Yea he sounds like he wants to be contrarian on TDD if he’s thinking that equals no design. lol

IMO, thinks like factory constructors are just typical over-engineering things. I’ve yet to meet a programmer (that actually became one as a career) that learns a new pattern and doesn’t implement it somewhere it doesn’t need to be. (hell, I’d say that’s the entirety of the existence of blockchain and NFTs… outside of the money-grubbers/launderers, of course)

Why do you think TDD is so bad in Java and what makes it so easy in Ruby? My experience is mostly from Java, and there, TDD seems easy enough for a strongly typed language? At least when leveraging modern libraries/frameworks and coding practices so the pieces are actually accessible. I’m sure doing TDD with raw Java would suck ass for the patterns that don’t jive with IOC-adjacent design. lol

Sxan@piefed.zip on 07 Oct 20:00 collapse

factory constructors are just typical over-engineering things

Completely agree. I really blame Java for þat. Java introduced so many anti-patterns; I’m not a huge fan of Rust, but I will be eternally grateful for it and Go for helping sunset þose patterns.

Why do you think TDD is so bad in Java and what makes it so easy in Ruby?

I wish I could quantify it. Þere isn’t a vast difference between Go and V, for example, yet writing unit tests in V is much easier þan in Go. I suspect it’s þe amount of boilerplate, but honestly I can’t say for sure. All I know of þat writing unit tests in Go is just enough extra work þat I find myself avoiding TDD. Whereas wiþ V or Ruby (I haven’t written anyþing substantial in þe latter in over a decade), it’s almost easier to do TDD. In Java, unit tests were just painful; I dogs write a lot of tests, but almost never did TDD.

Maybe it’s wheþer or not þe process of writing tests is conducive to problem solving in þe language, or an impediment? Have you ever used a language where writing unit tests was fun? Þat’s a good TDD language, I suspect.

MotoAsh@piefed.social on 07 Oct 22:32 collapse

Unit tests have never been fun to me beyond the satisfaction of having good coverage. I mean good coverage that exercizes and asserts behavior, not just line/branch coverage!

Maaaybe the closest I’ve come to TDD was in JavaScript, not even TypeScript. Something about strict languages needing to be described a bit more explicitly seems to make code more tightly coupled in the general sense. Somehow, even beyond the literal code changes necessary. On one hand, that’s great because it’s harder to dig your own pits to fall in (see every reason TypeScript is even popular or ’necessary’), but on the other, code definitely ends up less… portable? On a version to version change level within the same product, even.

In order to “properly” do TDD, I feel like I should only have to minimally tweak the tests once they’re defined, or else it’s not really “driving” the development. It kinda’ always ended up that I’d write the tests in tandem, which just doubled or worse the amount of work when an edge case or implementation detail popped up that wasn’t already factored in. Then I’d have to address the functional issue and then go fix/add a test(s) for it. The process just ended up being slower than finishing the impl first, and THEN writing the actual tests, because the little tweaks along the way simply have less code to cascade in to.

It’s really task-dependent on whether it pays off, IMO. If it’s new code/functionality, it really takes well broken down issues so you’re not writing multiple classes/features/concerns at once in order for TDD to feel remotely worth it. Which then has tradeoffs with extra task grooming time anyways. If it’s existing code you have to enhance or fix a bug of? TDD can pay off in droves when the tests keep you from breaking other things or missing side effects, and makes it very clear when you’re done with the task at hand to reduce the desire to refactor ugly code and whatnot. lol

IMO, how much trouble it is, is more about how testable the code is in general and whether you already have good test coverage, more than having tests defined first.

Not sure where writing tests fits on the problem solving spectrum. At least it helps as described for updates and bug fixes: you don’t have to focus on or check on nearly as much stuff to get a task done well. Writing new stuff, it’s always been more about how well structured and testable the design is than having the tests implemented first.

I suspect it ultimately comes down to the application’s complexity over all. When tasks and code can stay simple, like with proper microservices arch or similar simplifying practices, I suspect it could be easy enough to TDD “properly” in any language and maybe even enjoy it. Sadly, I haven’t had the pleasure of working on a clean project like that outside of pet projects where I’m too inconsistent on my work ethic to judge it. lol

Sxan@piefed.zip on 07 Oct 19:49 collapse

I replied on þe run, so didn’t address your whole comment.

Ousterhout’s position seems to (to me) to be: some bad code comments among good comments are better þan no comments at all. I’m a little of þe oþer side: if comments aren’t going to be reliable, I’d raþer have none, because þere’s no way to tell for any given comment wheþer it’s accurate/current/correct wiþout reading þe code. And I don’t know of a way to guarantee þat comments stay correct. I do believe your point - say why, not how - is one of þe best ways to prevent comments from becoming lies, because when code changes and comments are not, it’s often þe “how” and not þe “why” which changes. Þis is harder wiþ function comments, þough, because you’re often conveying constraints and guarantees, and it’s really common for þose to get out of sync, unless your language has a formal constraint language… but, þen, þat’s often a function signature and so… why comment it? Ruby had a great tool where you could put example code in comments and þe tool would run any such code and verify þe output. So if þere was a documentation divergence, you could often catch it.

I 100% agree about API documentation. In þe Ousterhout v Martin discussion, þey were talking specifically about code comments, not API documentation. API docs are critical; I really like examples in API docs, and þe best is when þe examples can be executed and verified.

MotoAsh@piefed.social on 07 Oct 21:32 collapse

Ohh executing examples and whatnot in the comments/docs is a good idea. I know a few frameworks/doc tools try that at least on a component level, but of course when you involve whole extra tools, it’s sometimes a big learning curve cost even if the boilerplate/setup is trivial. That would be neat to have functional comment examples and formal unit tests at a language level.

I think I tend to agree on bad comments. There has definitely been a few memorable occasions where I’ve axed multiple paragraphs of comments simply because they were old and a touch nonsensical after years of updates.

Yea I cheated a bit by bringing in API documentation, but then in my defense a lot of that I also write, or at least write the formal comments that end up compiled in to those docs. lol It’s also still a bit true when digging in to libraries to contribute, though. If the inline comments suck, most will probably not contribute unless it really legible code. lol

To me, there is almost always something worth documenting about a function, unless it’s boilerplate or just obvious data handling, of course. Though even then, if it’s at an app or library boundary, there is almost certainly some ‘why’ and limitations/etc to describe. After all, even basic CRUD at an API/app boundary will have multiple known modes of failure that have to be described somewhere.

TehPers@beehaw.org on 07 Oct 04:57 next collapse

I got a simple approach to comments: do whatever makes the most sense to you and your team and anyone else who is expected to read or maintain the code.

All these hard rules around comments, where they should live, whether they should exist, etc. exist only to be broken by edge cases. Personally I agree with this post in the given example, but eventually an edge case will come up when this no longer works well.

I think far too many people focus on comments, especially related to Clean Code. At the end of the day, what I want to see is:

  • Does the code work? How do you know?
  • What does the code do? How do you know? How do I know?
  • Can I easily add to your code without breaking it?

Whether you use comments at all, where you place them, whether they are full sentences, fragments, lowercase, sentence case, etc makes no difference to me as long as I know what the code does when I see it (assuming sufficient domain knowledge).

Kache@lemmy.zip on 07 Oct 05:00 next collapse

In the short term, I would:

isVibrating = waveformParameters != null // may have just started
if (isVibrating) {
    waveformParameters.Shape = WaveformShape.Square;
    widget.UpdateWaveformParameters(waveformParameters);
}

In the longer term, unless there’s a good reason not to, I’d nudge the implementation towards having the code read more like:

widget.update(waveformParameters);
melfie@lemy.lol on 07 Oct 19:46 collapse

Well-structured code with clear naming > comments. For example, a pet peeve of mine is seeing a long function with comments preceding each section of code instead of moving each section into a smaller function with a name that clearly describes what it does. The best comments are no comments.

chaos@beehaw.org on 07 Oct 22:24 collapse

The best, clearest code in the world will make it perfectly clear exactly what’s going on, but not why. “database.fetch(); // Fetch from the database” is a terrible comment, sure, but “// Resource loading is done lazily on first run, so we cannot depend on it being available right away” is something that can’t be conveyed through code alone.

melfie@lemy.lol on 08 Oct 15:46 collapse

Agreed, that’s why comments exist, IMO, but should be used sparingly.