27 comments

[ 3.3 ms ] story [ 75.0 ms ] thread
I see comments as sort of a fallback when I don’t have time or can’t think of a more readable solution to a problem. I believe this is the rational presented in the “clean code” book
(comment deleted)
I think you you have identified the source for this dubious concept's widespread acceptance.

Putting aside the question of whether this is only about comments -- the article is about documentation and the myth of self-documenting code -- suppose you have written a function. There are many things another programmer might want to know about it, such as its time complexity, whether it is thread-safe, what exception guarantees it provides (or simply what exceptions it might throw, and what they signify), whether it modifies state, and if so, when and what, whether its design is predicated on the system being in one of a subset of possible states... how much does a developer have to read to answer one of these questions? The answer is that, in general, it is not bounded.

Now, the amount you would have to write to answer all possible questions is also not bounded, but -- and this is the real point -- you can choose what to document in order to maximize the usefulness of the function. And it is rare that the optimal solution is no additional documentation beyond the source code, at least for non-trivial components of non-trivial software.

Comments in code often only describe the task at hand, and rarely ever the big picture of how code fits together or why. Documentation, test cases(and comments) and internal FAQs cover these to some extent, but are disassociated from the code itself.

IMO to think inline code comments are as important as they are misses the forest for the trees; I want to see the solution to these bigger problems.

Moreover (and this is a separate problem altogether) most code comments are text based-Are there examples of non text commenting that have taken off? Often an algo or structure is so much better described with an image or video- yet most comments are text based.

There’s much to come in the way of comments- there must be.

> Often an algo or structure is so much better described with an image or video- yet most comments are text based.

I've written my fair share of ASCII diagrams. I've also smuggled links to wikis into my doc comments for e.g. cross platform abstractions for on-screen keyboards. Then I can link all the screenshots I want. Or to gifs/videos for shader effects demos. Or to a series of images and code snippets for tutorials, or ...

Heck, if you're using markdown doc comments, there's no reason you couldn't embed image URLs that get expanded directly into inline images by the doc generator...

If I need to document something in a way that doesn't fit into a comment, I'll add a separate document to the source repository and then add a comment that references it in the code. If you're using something like GitHub that gives you a Web interface that renders Markdown files, the ergonomics of doing it this way are really pretty good.

I feel like this is the best way to document "big picture" stuff in general. You don't want to just have the comments, because, if you carve it up into a bunch of little vignettes and scatter them around the code, it's no longer a big picture. But external documentation alone is also insufficient. If you don't mention when it's relevant, at the spots where it's relevant, then you can't rely on people to know when and where to look for it.

I mean, sure, if you have what looks like a simple getter function for temperature that doesn't support negative numbers, represents it as tenths of a degree in ints, and can only be set by a recurring background process, you probably should be commenting that.

Good code doesn't document itself, but bad code with comments also don't document itself, and good code with comments has a surprisingly short half-life of self documenting efficacy.

Nontrivial public classes should include solid comments, along with interesting methods, but you don't need to be commenting a simple getter or a parameterless constructor.

Code doesn't live in isolation. Often we are forced to solve problems that were created a layer below our code. In a way this can lead to unintuitive code as well.

If I do this, I throw in some comments about my reasons to do so.

"Comments are bad" is the "fake news!" of programming. Yes, it's reasonable to point out that there are comments that are either inherently bad or become counterproductive as the code moves on without them being updated. OTOH, using those minority examples to cast the whole idea into disrepute is just cherry-picking to rationalize one's own faults. Good commenting complements good code structure, naming, etc. to make code easier to maintain/extend.
I've seen code where bad/counterproductive doc comments are the majority examples, not the minority, as a lazy way of meeting some "100% doc comment coverage" mandate. While it still doesn't and shouldn't cast the whole idea into disrepute, they're useful cautionary tales, and a way to help distill down "what makes for a useful comment?" by showing what isn't useful.
I could only wish that excessive and redundant commenting was somewhere in the top five problems I face in writing software.
I mean, my experience is that these aren't actually opposing problems; it's wretchedly common to find a file where informative comments are scarce, uninformative comments are too numerous, and all the substantive comments are out of date.

If code had one problem or the other, we might be able to judge which issue was more common and push people the other way. As is, it tends to have all three diseases at once and I'm not sure what the cure is except screaming "do better!"

It is also not uncommon to have code where the various elements do not live up to the claims made in their identifiers.

If a programming language is expressive enough to create self-documenting programs, it is expressive enough to imply things that are not true.

The point is that if you are not going to trust the words a programmer puts in documentation, then you can't trust the meaning of any words they choose to use in identifiers, either - but if you don't trust the words they use for identifiers, then the code is not self-documenting in any meaningful way. The observation that documentation can be bad does not justify the claim that good code never needs documenting.

> As is, it tends to have all three diseases at once and I'm not sure what the cure is except screaming "do better!"

Code reviews with people who give a shit helps. I'll commonly suggest specific areas for comment in reviews. And I'll mercilessly delete redundant or just plain wrong comments. Sometimes they were clearly copy+pasted from another method (and thus not right in the first place). I assume they wanted to copy the doc-comment syntax and meant to fix it up later, but then never did...

> those minority examples

The problem is that those aren't the minority. Comments are like lawyers; there are plenty of good and necessary ones, but 90% of them give the rest a bad name.

I like to use comments as a sort of punctuation for my code. It's kinda a subtitle for lines that are a bit more involved. Also gives me a chance to reference JIRA tickets which have even more information about why the code is doing what it's doing.

Still drives me nuts though to see 10-20 getters/setters in a Java Bean, each annotated with the Javadoc "Gets variableA"

<rant> Why do i have to scroll down a full page to see any text and then when I do, why do I have to zoom out to read it properly? </rant>
Good unit tests work too. :) - But are not a replacement.

Saying why a constant's value is 4 gives business context, or why you pass null as the last param of this function, or why we don't wait for this function's promise, etc.

I tend to flip between two extremes:

Code where I write few/no comments, because the API is intuitive, straightforward, and free of weird edge cases or unexpected differences, with any constraints well encoded by the type system, with everything relatively obvious from naming conventions, etc.

Code where I write more comments than code. It might still be a good API, but I can't fully encode the edge cases into the type system, or maybe there's some subtle internal edge cases, or it's unobvious why you would need some code that happens to be a bugfix, or [...]

I see code comments as useful for two categories of things:

1) Information that is available by reading the code. In this case, the comments serve as a short-cut to understanding the code. They might be explaining what an obscure flag does, noting the name of the algorithm being implemented, or giving an overview of the structure of the code. You could learn that by reading the code, but reading the comment is faster.

2) Information that is _not_ available by reading the code (or is unlikely to be corrected derived by reading the code). For example, the reasoning behind decisions can only be guessed at by reading the code alone.

Code will definitely tell you how something's done with enough energy.

It will NEVER tell you WHY everything was done a given way - which is where the comments come in. The trick is to keep them updated, versioned and restricted to WHY.

Nothing worse than the template java comments that are filled in for the sake of it. I can't think of any codebase where I was able to reasonably rely on those anyway: except for publicly released API.

Comments also need:

The CONTEXT, often non-assertable non-functional assumptions.

The WHY NOT, the non-obvious omissions, because code that isn't there cannot be self-documenting.

I try very hard to avoid writing clever code. Instead, I strive for maximum clarity and simplicity. But on rare occasions I do have to write something clever, because the simple and clear thing would either be less efficient, or much much longer, or involve lots of boiler plate code reuse.

In those cases I may have to explain not only why I wrote the clever part, but also how it works, because it might not be immediately obvious unless the reader digs really deep in to the code to figure it out. I'd rather save them (or me at a later date) the trouble.

Yes, this risks having the documentation drift away from the code if I ever change the code (or if I make a mistake in the documentation), but it's a risk I'm willing to take in small doses.

I really like ghc's comment style: all toplevel functions have a comment that describes usage and interface. Comments about implementation details and gotchas are floated out and referenced. https://ghc.haskell.org/trac/ghc/wiki/Commentary/CodingStyle

    prepareRhs :: SimplEnv -> OutExpr -> SimplM (SimplEnv, OutExpr)
    -- Adds new floats to the env iff that allows us to return a good RHS
    prepareRhs env (Cast rhs co)    -- Note [Float coercions]
        | (ty1, _ty2) <- coercionKind co    -- Do *not* do this if rhs is unlifted
        , not (isUnLiftedType ty1)     -- see Note [Float coercions (unlifted)]
        = do { (env', rhs') <- makeTrivial env rhs 
             ; return (env', Cast rhs' co)  }
     ...more equations for prepareRhs....

    {- Note [Float coercions] 
    ~~~~~~~~~~~~~~~~~~~~~~
     When we find the binding
         x = e `cast` co
     we'd like to transform it to 
         x' = e
         x = x' `cast` co     -- A trivial binding 
    There's a chance that e will be a constructor application or function, or something
    like that, so moving the coerion to the usage site may well cancel the coersions
    and lead to further optimisation.
     ...more stuff about coercion floating... 
    -} ``
I try to live by a few simple rules regarding code vs comments. Here are ten of them. Some of these are already in other comments, in a different form.

1. Code should show not tell. That means the comments I write are typically at the beginning of a class or function rather than scattered throughout on separate lines.

2. Code shows how, comments instruct why. A good comment includes the reasoning behind the algorithms and tradeoff choices chosen by the coder.

3. Good comments include an example of usage

4. Good comments are compatible with an automated documentation framework, such as Javadoc, Doxygen, Rustdoc, and Pydoc

5. Good comments do not include a modification history. Version control and changelogs can handle that.

6. Kill your darlings - Good code follows the KISS principle, and is easily understood, as well as easily maintained

7. Good code is SOLID - This is part of Kill Your Darlings, in a way. To be easily maintained, in many cases, you need to follow SOLID. Here's a good article on SOLID: https://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principl...

8. Good code is short - I don't have a firm line count, because it's different based on domain, language, etc. I do try to keep each method/function to no more than a quarter of a screen of code at a reasonable resolution, so that's maybe around 15 lines. With modern optimizing compilers and VMs this will not be a large performance hit, if it's a hit at all, assuming you are following suggested practices. Martinn Fowler follows the same approach, as he explains here: https://martinfowler.com/bliki/FunctionLength.html

9. Good code has simple, but thought out, names. For the love of Bunnies and Bears everywhere, please do not make a function named similarly to GetFoobarEffectBarIndexFromFoobarSourceBarIndex ( a real function name changed to protect the ~innocent ). Btw, Bunnies and Bears reference is from a Microsoft practice, see https://blogs.msdn.microsoft.com/oldnewthing/20030818-00/?p=....

10. Good code breaks the rules it needs to in order to get the job done, so apply the above rules in context. In other words - make it work, make it fast, then make it elegant. You are the only one who can choose where to stand on any given trade-off line, and that is fine (as long as you know why you are standing there and can explain your reasoning). The surely wrong person is the person who says their way is the only right way in all situations.

I really wish Literate Programming had taken off. I loved that style.
The most important habit I have carried since my COBOL days is the need to always use self-descriptive variable names. Herewith, some recent variable mouthfuls:

     char machine_prom_name_string[TERM_WIDTH_WIDE];
     int memory_mapping_register[4];
     int timer_interrupt_active=0;
     WORD floppy_controller_clock=0;
     BYTE four_key_reset_enable_flag;
     XColor yellow, green;
Oh, and just to reinforce other people: Comments are there to explain WHY.