“Good Code Documents Itself” And Other Hilarious Jokes You Shouldn’t Tell Yourself

Code documentation — is there anything more exciting than spending your time writing extensive comments? If I had to guess, your answer is probably somewhere along the lines of “uhm, yes, everything is more exciting than that”. Plus, requesting to document your code is almost like an insult to your well thought out design, this beautiful creation you implemented so carefully that it just has to be obvious what is happening. Writing about it is just redundant, the code is all you need.

As a result, no matter if it’s some open source side project or professional software development, code documentation usually comes in two flavors: absent and useless. The dislike for documenting ones code seems universal among programmers of any field or language, no matter where in the world they are. And it’s understandable, after all, you’re in it for the coding, implementing all the fun stuff. If you wanted to tell stories, you would have chosen a different path in life.

This reluctance has even formed whole new paradigms and philosophies claiming how comments are actually harmful, and anyone trying to weasel their way out of it can now happily rehash all those claims. But, to exaggerate a bit, we’re essentially villainizing information this way. While it is true that comments can be counterproductive, it’s more the fundamental attitude towards them that causes the harm here.

In the end, code documentation is a lot like error handling, we are told early on how it’s important and necessary, but we fail to understand why and instead grow to resent doing it again for that same old teacher, supervisor, or annoying teammate. But just like error handling, we are the ones who can actually benefit the most from it — if done right. But in order to do it right, we need to face some harsh truths and start admitting that there is no such thing as self-documenting code, and maybe we simply don’t understand what we’re actually doing if we can’t manage to write a few words about it.

So let’s burst some bubbles!

Self-Documenting Code Doesn’t Exist

The usual argument against commenting code is that “the code should be written so well that it won’t require any further explanation”, which is actually hard to argue against if we’re talking about what the code does. Well-written code should indeed not require any comments to describe what the objective of a variable or function is.

// bad start:
int a = 4 * OFFSET;
// but don't use a comment to tell what it does:
int a = 4 * OFFSET; // initial foo value
// instead choose a name telling it itself:
int initial_foo = 4 * OFFSET;

Yes, a meaningful variable name makes the comment obsolete, but that’s really more a question of decent coding style than it is about documenting. The problem starts when this easily proven, albeit one-sided view is turned into the universal justification against any type of comments, including the ones that go beyond explaining the what, and focus on the actual interesting and helpful parts.

The thing is, having self-explanatory names for your variables, methods, classes, functions, modules, etc. doesn’t automatically describe the big picture of the code, nor does is necessarily tell much about the why and in what way parts. However, having a clear and well-written implementation tends to give the illusion that there’s no need for that either. And yes, after spending hours or days wrapping your head around the issue at hand, of course that code will make perfect sense in the very moment, even more so if you pack it all neatly into a reasonably sized commit or pull request that presents your solution in a condensed and coherent manner.

But how about in a month from now? Or outside the context of that self-contained commit? Or when approaching it with a slightly shifted mindset? How much details will you remember, and how much sense will it all still make then?

Software Is Hard

Of course, one can (and will) argue that “the code is right there, just read it and you’ll know”, and again, if we’re talking about what a specific block of code does, then yes, that attitude is justified. But for anything beyond that, digging through code is an unnecessary waste of time, and is essentially like saying a book doesn’t need an index, just read the whole thing and you’ll eventually find what you’re looking for. Do you really want to mentally parse every path some data could take to find out about its valid ranges, when a single sentence that takes a minutes to write and even less to read could just tell you directly?

And it’s not only about understanding other people’s code, or explaining to other people what you were thinking. How often have you caught yourself wondering what on Earth you were thinking when you revisit old code or fix a bug, or were surprised a git blame revealed your own name? Yet the very next time, it’s all forgotten, and you’ll be convinced once again that everything is oh-so-self-explanatory, and all the details are unmistakably obvious.

Software just isn’t fully and universally self-documenting by itself, no matter how hard you try. And that’s neither your fault, nor me trying to be a bully and question your abilities, but it’s simply about being human, and about underestimating both the full complexity of software and the volatility of our mind. Documentation isn’t about shaming and pointing out shortcomings in your implementation, but about countering the shortcomings of the programming language itself. Even the cleanest code ever written couldn’t explain by itself what you were actually thinking when writing it. Everything might be perfect and still do the wrong thing. Comments aren’t an alternative to writing clean code, but an inherent part of it.

Anatomy Of A Comment

Before we go into further details, let’s have a look at different comment styles first.

/**
 * Javadoc-style documentation comment.
 */
void foo(void) {
    if (bar > 10) {
        /* regular comment */
        ...
    }
}

Regular comments are just that: comments as defined by the language itself. As a rule of thumb, they should be used sparsely as they tend to explaining what the code is doing.

Documentation comments on the other hand are used to describe global variables, functions, and modules (plus their object-oriented counterparts) from the outside point of view. Inside a function body, they basically turn into regular comments and tools will generally ignore them. As a good practice, if there’s something worth telling on the inside of the function, see if it could be worked into the function description itself.

Documentation comments are essentially regular comments with some extra accessories, such as an additional forward slash /// doc comment, exclamation marks //! doc comment or /*! multiline doc comment */, or an additional asterisk as in Javadoc-style comments /** doc comment */. Despite its name, Javadoc as a commenting style is also supported by other languages and tools, and will be used for the examples in here.

Of course, you can also just use regular comments and forget all about those funky tags, but the advantage is that documentation generators such as Doxygen or Sphinx can easily create PDFs, HTML, or man pages straight from the documentation comments, and most modern IDEs have extra support for displaying them, saving you a mental context switch to the actual implementation — provided there is some useful information available.

But aside from triggering comment post-processors, the format of the comment isn’t important. What matters is what you’re saying.

Redundant Comments Focus On The Wrong Information

So, we have established that we shouldn’t document what the code does, but rather why and in what way it does, But what does that really mean?

A common reason that people loathe things like documenting their functions is that “they just state the obvious” and are therefore redundant. And reading the average doc comment, it’s actually hard to argue against that, especially when it comes to encapsulation in object-oriented languages. The average description for some simple get_temperature() function would probably look something like this:

/**
 * Returns the temperature.
 */
int get_temperature(void) {
    return temperature;
}

That comment does indeed not add much value, it essentially just repeats the function’s name and therefore only tells what it does. That’s not what we want. What we want are the details that the code doesn’t tell us.

It’s easy to think that the whole function is just so simple, there is absolutely nothing useful to comment in the first place. But then again, nothing is ever really simple in software, and if you look close enough, you will find that every function has something worth writing about that isn’t instantly apparent from its name, or even the code of a simple one-liner.

/**
 * Returns the temperature in tenth degrees Celsius
 * in range [0..1000], or -1 in case of an error.
 *
 * The temperature itself is set in the periodically
 * executed read_temperature() function.
 *
 * Make sure to call init_adc() before calling this
 * function here, or you will get undefined data.
 */
int get_temperature(void) {
    return temperature;
}

Turns out this seemingly simple, albeit fictional function had plenty of extra information to write about after all. Not despite being simple, but because of it. None of the information could have been obvious and self-explanatory just from looking at the code, including the additional information about the internal data handling and program flow. Sure, digging deeper into the code would have eventually revealed the same information, but also wasted a lot of time along the way, not to mention the unnecessary mental gymnastics it might take.

Others might say that those are implementation details that have no place in documentation. But why? Why wouldn’t you want to state those implementation-specific details that will ultimately make it easier to understand what’s going on?

Adopting the mindset that every function has something to tell, that there is always at least one detail, side effect, exception, limitation, etc. worth writing about, means that you might have to look at it from different angle to actually find it. To be able to do that, you’ll inevitably have to confront yourself more and more with the hidden details of your code, possibly discovering corner cases that you haven’t even thought of before. As a result, documentation doesn’t only help future readers to understand of the code, it also helps the writer to gain better knowledge about its internal details.

And if you really cannot find any useful information to add, you should probably ask yourself why the code is there in the first place. What’s the justification for having it? And that justification is exactly the information to add then. The previous example could have gone in a different direction:

/**
 * Returns the temperature.
 *
 * This is for testing purpose only and should
 * never be called from a real program.
 */
int get_temperature(void) {
    return temperature;
}

Note that this is still the exact same code as before, which brings us to another problem with “seemingly self-explanatory code that is too simple to comment”: it can be vague and ambiguous, leading to false assumptions and possible bugs. Pointing out these details and eliminating potential ambiguities can be vital in terms of code quality, and it can be argued that this actually makes the documentation an essential part of the code itself.

Again, every function has something to tell that is not immediately obvious without looking further into the code. Naturally, some of those inconspicuous details are more relevant than the other, and not everything a function might have to tell is necessarily interesting. But is there really something like too much information? The list of cognitive biases is long, and just because something is obvious to you in this specific moment, doesn’t meant it is for the next person handling your code — including your future self.

Make Comments Part Of The Code

Now is a good moment to throw in another favorite of “comments are bad” rhetoric: they get outdated when the code changes. Let’s be real though, that’s just a seriously lazy excuse, it’s not like code is usually written with a lot of consideration about ever having to touch it again in the future. Once committed and merged, the code is final and perfect, to remain as-is for all eternity.

The bigger issue with code documentation is that it’s seen as something that exists beside the actual code, completely decoupled from it. But if we start seeing it as actual part of the code, a complementing entity and not some dumbed-down summary for anyone incapable of dealing with the real thing, it will become natural to simply adjust it whenever the code changes.

And yes, that includes private methods and static code in C. It’s such a major misconception to claim that they contain irrelevant implementation details that require no documentation, or are anyway not exposed to the “consumers” of the code. Well, at least the latter part might be true if we consider the users of libraries, APIs, and the like, but what about the developers? After all, private functions are usually the place where all the interesting details happen, the number crunching, data juggling, all the little secrets — and with it the parts that usually require the most maintenance.

Scope should have nothing to do with the relevance or existence of information, but this just shows how the general mindset towards code documentation sees it as something that is intended for anyone else but ourselves.

Breaking the Circle

Nobody likes bad documentation, but avoiding documentation altogether cannot be the solution to it. Fixing the dysfunctional relationship between developers and comments is the only way to improve the situation, and seeing them as a fundamental part that co-exists with the code is a good first step.

No doubt, it will take practice and getting used to that way of thinking, but in the long run, it can only benefit the general understanding and quality of the code.

In that spirit, here’s one final, redundant comment:

/* You have reached the end of the article */

159 thoughts on ““Good Code Documents Itself” And Other Hilarious Jokes You Shouldn’t Tell Yourself

      1. Not a gripe about comments, but this is my favorite related gripe with poorly written user manuals.

        There seems to be a thing these days where somebody who knows nothing about the product/software/widget or, gets to write the manual. Alternately, I suppose, maybe they’re just so deep into it that they feel it’s obvious.

        Either way, you get instructions like “Glarfarb button; this is the button for glarfarb”. Then you look in the index under “glarfarb” and the page lists “Glarfarb: See glarfarb button”

        Arrgh. (See arrgh button)

        1. In primary school I didn’t know what a word meant. Teacher said look it up in the dictionary (which we just happened to learn how to use in the same week). word1 see word2. On the other page: word2 see word1. When I complained to the teacher she got mad.

          1. Information is relational my friend… all of the core “axioms” can be decided arbitrarily. Like F=MA, you could say an acceleration is a force divided equally into a mass, mass is the quality of preventing movement, or you can say that a force is an acceleration times an mass amount.

        2. Which is why, when a colleague and I were developing a program we intended to sell, we developed the algorithm together, but I wrote the code, and he wrote the manual.

    1. I made this one up. Feel free to spread so it goes viral. Hope you enjoy:

      “Good Code vs. Bad Code:

      Good code is code that is simple, concise, easy to read, and self-documenting, like ‘Hello World’

      Bad code is code that is very complex and very difficult to understand, like operating systems programming.

      Lesson: Do not comment your bad code because it may get out of synch with the code when the code changes. Write good code instead.”

      – James Walker

  1. “I’ll make a comment”
    If you comment too much, then you risk misleading comments when the code changes and you forget to update.
    To combat this, I make variable names that are longer, and more descriptive than most.

      1. It’s for example about the comment:
        * Returns the temperature in tenth degrees Celsius
        * in range [0..1000], or -1 in case of an error.

        Comments with such implementation details make code impossible to maintain.
        In a later stage you may decide Temperature should be in Kelvin, or with 0.01 degree resolution, or support for negative degrees Celcius or whatever.
        When that happens (and those things happen a lot). then the code is out of sync with the comment, which has become misleading. When you look at that example there is no way to tell whether that comment is actually conveying the truth, or it has already become out of sync with reality, and you are a fool if you believe it.

        If there is no comment, or you wizely ignore the comment, click on temperature, and “goto declaration” or “find uses” such as any decent IDE has (arduino java contraption is NOT a decent IDE), then you can see what that Temperature value actually does, and how it’s made.

        If you really want to add such implementation details, they should be at the place where those implementation details are implemented, and at that location they are probably redundant.

          1. Note that in many modern languages like Haskell, Julia, Scala, Idris, Kotlin?, etc… you can put the units of measurement into the type system itself and the compiler will make sure that the documentation is checked. Refinement and dependent types can be used to present the constraint that the temperature is less than 1000, or in python/JavaScript you can have post & pre conditions implemented as runtime contracts that are implemented as decorators. Whenever you can represent comments as types, “decorators”, identifiers its better do so, as comments violate DRY principle and can’t be automatically checked.

        1. Those aren’t implementation details, they’re something the user depends on. Implementation details are whether the temperature is coming from an I2C sensor or from an ADC. The user doesn’t care, the user does care if you change scales or resolutions. If someone’s going to make that big of a change there’s no excuse not to update that in documentation comments whether it’s remembered by the programmer or by the code reviewers.

          There are also many times in my work where I’m using vi in a ssh terminal to edit and test code in quick iterations on a target and those fancy editor features aren’t there.

          1. int get_temperature(void) {
            return temperature;
            }
            There is zero information how temperature value is created. A comment that talks about implementation that is not coded there will be left unupdated because there is no reference to know that this particular comment needs to be updated when the code changes. With that concrete example you are saying that the whole code base needs to be checked for comments in case some other comment mentions the code you just changed. No IDE will pick that up. As such the commenter above is absolutely correct to call this out. This does not invalidate the point of the article but that example runs counter to the point being made or rather another point would need to be made.

            Do not comment about things that your code does not do there and then. ‘return temperature’ hides the implementation, the comment talks about the implementation – the comment does not belong there, it will be orphaned and misleading. This is WORSE than not commenting.

            About ssh and vi, well cant you sshfs instead? I mount my externals and use my fully configured vim that does have all the features. The down side is that for execution you need a second terminal to execute the changed code, but ill take that over raw vi everyday.

          2. Definitely agree, comments save wasted hours and are to be updated with code, and caught in review.
            Ideally one can reference the other functions/types in you comments (CREF in my language) and likewise in the opposite directions, this will mean likelihood of getting updated will be much better.

            Also you should install a virtual frame buffer, vnc and get a real IDE like vscode ;)

            Plus thanks because I learned more about cref today:
            https://stackoverflow.com/questions/41255399/what-is-the-t-for-when-used-in-a-cref

        2. I would argue that some of that pain can be relieved by using the newtype paradigm. Basically by creating 1-variable structs with some helper macros you can completely avoid these issues. For example you would use something like this https://gist.github.com/sw17ch/209588 to create a newtype [NT(int, CelciusDec) // Celcius in tenth degrees] and then use the helper macro VALNT(x) to get the inner value. Then you would create helper functions or macros to convert between types (CelciusDec -> KelvinDec). This forces those types of errors into the compiler and also most half decent compilers will optimise away 99% of the abstraction.

          Now for things like initialisation responsibilities, sure those belong in comments but it may be more responsible to embed fail/panic on no_init or self initialisation within the functions themselves.

        3. I mostly agree that this particular set of made up comments isn’t good. The author selected a poor example to try to illustrate their point. The information in the first line is especially suspect. If you find yourself writing this sort of disambiguating information in a comment on a function, that’s a sure sign that the function could be better named and perhaps as another comment indicates return a custom type that encapsulates the deci-Celslius nature of the data.
          The name of the field that is returned from the function is poor for the same reason.

          Whether or not to trust the comments*, and where they belong is another thing entirely. I don’t agree that consumers of a piece of code should have to explore the implementation in order to properly use it. The proper place for comments explaining how to use the code (in particular these JavaDoc type comments) is on the code to be used. (whether that is a public interface or a private function) Then, if you are using a decent IDE, these comments will be available to the user at the call site (how it is presented is different from one IDE to the next).

          In this particular case, I probably wouldn’t completely trust code with this comment attached to it. In one line it indicates it returns -1 to indicate an error, and in another place it indicates it can return uninitialized values. The combination of these would make me wonder what exactly the implementation does. (if nothing else, I’d expect it to return -1 if the prereqs of calling init_adc() and read_temperature() were not satisfied, as failing to call these first should be considered an error.)

        4. Exactly, comments like that are terrible, they have no relation to the logic of the function and will not be updated when underlying functions change. Don’t be lazy, look into implementation and don’t rely on comments. Comments lie, code doesn’t ;)

        5. Paul,
          try checking the ‘Mars Climate Orbiter’ for such a problem where the failure to note which units were in use (software that calculated the total impulse produced by thruster firings) caused a minor problem (OK so it was a really big failure – total mission loss, but whatever).

          One needs to communicate to other (human) users of the code, not just the compiler and computer. Communicating well with people is a hard problem. We are bad a training folks at how to provide good comments, often caused by the way we breakdown tasks into (apparently) independent sub tasks.

          The issues can also affect pure code where perhaps some aspect is ‘implementation defined’, and won’t transfer between systems and compilers (e.g. the size of ‘long’ vs size_t).

          1. Note that the Mars Climate Orbiter problem could have still happened *EVEN* with comments: if the comments were wrong. The compiler never checks the comments. To prevent the problem you need to represent the units of measurements as types. def checkTemperature {temp Celcius.

    1. i think that variable length should be proportional to how much stuff actually sees that variable. a local variable in a function for example could be one character long. if it was global it would be significantly longer even though it might contain the same information. any information vacuum would be made up by context.

      i also have different variable name formats for different scopes, but i still need to put comments there to explain what those are. hell all that stuff should be in the same file as main().

      1. Variable names should never ever be single characters.
        Too easy to mistype, too hard to refactor.
        A common and very usefull feature of IDE’s is to highlight use of some variable. This really helps when browsing through unknown code and getting an overview of how things work.
        For example a for loop with a weird loop counter. The counter is obviously weird because it is used in the loop to do something, but what?
        If you highlight that var in your IDE you have an instant overview of it’s uses. IDE’s tend to need at least 3 characters for this function to work properly.

        1. Eclipse (nearest IDE to hand) has no problem highlighting all uses of a single letter variable.

          Single character variable names are fine as long as the scope is small, and the character has a strong connotation. For example: i, j, and k for loop indices; x, y, and z for coordinates.

          1. I was taught to use double-letter loop variable names for highly localized loops: ii, jj, kk, etc. As the beginning of the loop is always only a few lines away, there shouldn’t be any confusion, and no other variable names are allowed to be double-letters.

        2. for(x=1; x<10; x++)
          If instead of x, you use my_favorite_variable_name_this_week, the for loop control won’t fit on one line. That is not good.
          Unless there’s a good reason to do otherwise, code should be written in chunks that fit on a single page without scrolling. It’s much easier to understand something if I don’t have to frequently scroll around looking for details.

          1. Edsger Dijkstra was perhaps the first to observe that making a program a computer can execute is far less difficult than making one that can be encompassed by the human mind, and it’s impossible to achieve reliability or maintainability if your code can’t be understood six months after you write it.

            But all career programmers eventually stumble on this simple truth, one way or another. It’s essentially a tautology to say that If it’s written well, it’s understandable; the style and format are purposely suited to human comprehension.

          2. Its 2021, why not use map, filter, zipWith, reduce, forEach to have a “self-commenting” loops? A for loop is a “specialized” goto pattern, why not specialize the loops even more?

  2. ” And it’s understandable, after all, you’re in it for the coding, implementing all the fun stuff. If you wanted to tell stories, you would have chosen a different path in life.”

    And yet it’s called a job for a reason.

    Anyway if software creation is a process then there’s a lot of useful information from all the architects on down for documentation, and it doesn’t start and end at the IDE.

  3. It’s really important to remember that what is obvious to you isn’t to others. If you’ve ever proofread your own work, and then had someone else proofread it, you’ve likely seen that they pick up mistakes that you glossed over, because you knew what it was supposed to say. The same can apply to code. You may understand it perfectly, but that doesn’t mean others will.

    1. Equally important: what’s obvious to you *now* won’t necessarily be obvious to *you* two years from now, when you’ve moved on to other projects, but there’s a bug fix or enhancement that has suddenly become the top priority.

      I always try to remind myself, “think of the poor sap who will have to maintain this code”–you might be looking at them in the mirror.

      1. Upvote – that poor maintainer is almost always me anymore, and I like me enough to make it easier.

        I’ve been at this long enough that typing is cheap. I’ll put a whole paragraph above a routine, not only like the example above but some of “what was I thinking” to organize things this way in the first place. Also, one of my more complex home projects involves multiple machines and big chunks of code I didn’t write – but which has config files (web servers, sql databases, various daemons that need systemd service files…). I do duplicate documentation, which yes, sometimes gets a little out of sync, but still it’s better than not – especially when things have to have the same name in some obscure config file in /where/did/I/put/that/one/dumb.name.
        Or where /does/the system/setup/require/this/to/be/because/this/path/is/in/some/other/obscure/setup/file/named….

        My solution to that so far recognizes that no one set of dox is good enough for a fading memory of 3 projects ago, so I have a local setup with phpbb on which I have threads…one major forum for the whole project, with sub-forums for each piece. By doing things in such a hierarchy, I can put philosophy up top – which does what when and why – and the details of “how” in the separate threads. Which all quickly become more than one post as I add and change things – I just leave the original stuff in too – I may want an example of how to do sometrick later on, but the latest is always the last post, and a little local forum makes putting that in nearly effortless, so it gets done.

        I’m probably duplicating someone else’s config management work doing it this way, but if so, my defense is all the other methods I’ve tried aren’t as well tuned to my personality and attention span.

      2. I think this is a very important reason to do meaningful names etc etc AND documentation – it might be YOU looking at the code a few years later trying to figure out WHY you did things a certain way.. I’ve still use code that I wrote almost 30 years ago (the advantage of C/C++ :-) ) and there is no way that I can remember what I was thinking when I wrote it… And it doesn’t matter that the code is easy to follow, as by itself that doesn’t explain the WHY.

      1. Right? I think if people applied that same argument to code, no one would ever write code. Granted, I had one person tell me after a talk that all of their error reporting was via email if their client app threw an exception.

          1. 99 replies and still going! An article like this doesn’t even need to be read — it is just the kick off to a wild melee in the comments and that is what is most interesting anyway. Some people are convinced that they know the one right way to do things and want to be little Hitlers. Others recognize TMTOWTDI (Tim Toadie — if you know your perl books). Until you have been coding long enough to have to go back to your own 10 year old code and make changes, you really just don’t know yet.
            Note that someone wanted to be a language nazi and condemn the use of “to code” as a verb. But language is plastic and continually evolving.

            One thing for sure — all the comments in the world won’t rescue bad code. And I’m not talking about buggy code.

      2. “better have no comments than wrong comments” — Disagree

        Comments express intent, code expresses implementation. Someone once pointed out that if the code and comments disagree, they are both wrong.

    1. Interesting that everyone seems to think you should comment code.

      I find it’s much more productive and robust to write the comments first and then code the comments. Top-down commenting and then code your way back up. That way, there are never any surprises, and comments are easier to debug at the design stage.

        1. Knuth’s talks about it sounded so good, I looked at some of the source code, and decided, you know what, literate programming is way too esoteric, indirect, and confusing for me; I’m gonna stick with C. It is much easier to read. Sad but true.

      1. If you twist this idea a little more then you end up with test driven design. Instead of writing what the code should do you test that the output of the code is the right thing. You first write test code that checks the output and then you write the code so that the test passes. If you find a bug, you add a new test that tests for that bug and then you update the code – this makes sure you dont reintroduce the bug later when you have forgotten that no you cant take this clever shortcut that seems so obvious.

        Writing a test first forces you to think about what the code will do. Rather than being a comment it is actual code where all the rules of good code apply. You have full power of the IDE to use. The suite of tests you write becomes the documentation. It is allways up to date if you follow a few simple rules and you can take it further and use it to generate documentation.

        If you also use advanced features of git to keep your history pristine then you can take that new test and use git to go back and check all the commits and find out when or by whom the bug was first introduced! (Git has an inbuilt feature for doing just that – forgot the name – its not a thing you do manually)

        1. I was thinking about TDD, too. My only problem with it is the “test is documentation” part. I would say that one still needs to document the tests. I’ve seen some unit tests really freaking complex!

          And the Git function you are looking for is “bisect”. A thing from the heavens. Thanks god it’s been a while I don’t need to use it, but it’s always in my toolbox ;)

      2. Yeah, code the comments.

        One advantage is you can then code in any language. I’ve always thought that I write code in English, then translate it.

        I mean, in my head I might think, “Open the file, read it, parse it then close it, with error checking along the way.” That thought should become a comment.

  4. When I was an undergrad programming student, I sometimes used misleading comments to obscure the fact that I was using special-case code when I couldn’t figure out how to get the exact results my instructors required. As I recall, I always got away with it, so I guess my commenting skills were pretty good.

      1. If you had been a university professor when I was an undergrad, you’d almost certainly be dead by now.

        I sometimes wondered, especially when I really couldn’t find another solution, if my “special case” approach was actually the one intended—to teach us to think outside the box, or something like that. But I was never brave enough to ask my instructors, and they never let on. Probably just wishful thinking anyway.

        In my even younger middle school days (roughly the Neolithic Period), I used to get A’s on my book reports after reading only the first and last chapters (especially Charles Dickens…). So, you can see that I learned early on the value of a good shortcut.

  5. It’s dangerous to codeand comment when you’re in a punny mood and hungry – I remember a code line I wrote just before lunch that whet like this:

    call fork ;get another byte

  6. For mid experience programmers good code should document itself.

    Once you start getting into the more esoteric algorithms and structures it starts getting a bit harder to parse at a glance.

  7. Don’t document your code, code your documentation. Besides, more often than not I made use of very very good libraries without ever having to read their documentation because everything was 100% self-explanatory. Which is a natural result of well thought-out code based on well written-out conceptualizations.

    1. In my first college programming class we were required to turn in a version of what we were going to program that was nothing but comments. It had to explain what you were going to do at every step, and why, It forced a better design because you would catch things in the commenting phase. Only after the prof had approved your comment program could you go ahead and add in the actual code.
      It really helped us to see the value of good commenting.

      1. In the industry you get the same result by doing test driven design. The idea is similar but instead of writing comments that mock your code, you write test that check if the output to given inputs produces the correct result. It does not check the internal logic but assumes a black box and checks the interface.

    2. Code will never answer the “why?” question, it only tells the “how?” part of the story. Your code is never “self-documenting”, and I recommend that you stop reading whatever that pathetic idiot Uncle Bob had to say on this matter.

  8. # It was hard to write. It should be hard to read!
    A comment I left in a stupid little script I’d written. I taught myself an important lesson when I found the script months later; Past me is an unhelpful dick, even to himself.

      1. One feature of being on the autistic spectrum is that you can’t understand other people very well because you have an impaired ability to what’s called the theory of other minds (automatic assimilation/simulation of other minds). Instead you have a persistent illusion that whatever you know and feel now is what everyone does – the autist is a natural born solipsist. It’s said that the first reason a baby cries is to gain attention. The second reason it cries is out of frustration when it realizes that there is a world outside of itself, and that world behaves in uncontrollable and unpredictable ways. Then it learns the boundaries between self and other, and learns to predict the other. The autist never quite gets that far.

        The tragedy is that this phenomenon applies to the past and future selves of the person as well. If you understand something now, you can’t imagine a time when you didn’t understand it – equally, you can’t imagine a time when you won’t understand it anymore. When things change, this causes stress and frustration, which shows up as a tendency for OCD – things have to be the same way because otherwise your sense of control is lost and you don’t know what’s going on anymore. It’s like some invisible force suddenly grabs the wheel of your car and starts steering – except it’s happening to your person – of course you get incredibly scared, frustrated and angry.

        This makes for rather peculiar behavior in “geeky” people. For instance, not commenting your own code because obviously you understand it and so should everyone else (or they’re stupid, wrong, pretending not to understand, evil…), and then when you forget and have to re-learn it, not comment it again because obviously you understand it now, and so should everyone else…

        A lot of the bad habits of what could be termed “technically minded people”, or whatever you want to call us – nerds – is because we’re all a little bit autistic.

  9. I propose that a law be passed, that all source code be commented, to the best of the programmers knowledge, This includes malware, virus’s , self modifying code…

  10. Words I try to live by: If you don’t know enough to write documentation for a function, then you don’t know enough to write code for the function… How many times I’ve been saved by documentation of what a function does when staring at year-old code that might as well be Sanskrit to me now…

    1. In the same vein, comments aren’t just for other people; they’re for you, too.
      Nothings worse than having figured out the interplay between a bunch of constants and variables you had figured out for a specific purpose.
      I’m only a hobbiest coder, so when I come back to a project after a few months without good comments, I basically have to relearn what I was doing, with good comments I can see what I was doing with new eyes to learn and improve.

  11. For assembly, never had an employer that wanted to see embedded comments – mostly wanted flow chart and a product that conformed to the SRS.

    Python docstrings?

    For C/C++, the best code docs are the test contract and associated harnessing.

    Code test doc formalization was first attempted with IEEE829, which got folded (or fooled) into the IEC29119 series, yet another joyful standard to behold. Test case derivations? Methinks test documents are more important than embedded code comments in an industrial environment. For your personal project? – just write whatever shit occurs to you. But this will earn derision once you post your shit on github. Do not care – as have taken perverse and sick pleasures in posting stuff on my git account that will fully and completely anger the sensible person. Also do much evil stuff with the respective comments on schematics. Bad people have more fun.

  12. Good comments don’t just say why you are doing ABC. It also says why you are not doing XYZ. It is a guide to the architects train of thought. An explanation of both chosen and rejected solutions. If the rejected solutions and reasons are not included you can be sure someone out there will reinvent a former failed path.

  13. Good vs bad code commenting almost always comes down to choosing the appropriate level of abstraction to choose when writing the comments. Bad comments come from not abstracting away enough or abstracting away too much. If you don’t abstract away from the individual commands enough, you find yourself commenting every single line, and then your code is as clear as the tax code: it’s all there, but it’s impenetrably dense. Abstract away too much and you find yourself writing comments/documentation that tries to envision every way in which the code might be used, trying to get inside of the heads of every person who might use the code. Choose the right abstraction level, and suddenly your are writing comments that describes what the code as a whole (meaning a function) does, what precautions should be taken, etc.

    It’s good to look at the documentation for physical components sometimes. Look at a simple bolt. The documentation will tell you the head type and size (i.e. what other tools you need to install it), the material properties such as alloy, hardness shear strength, tensile strength (i.e. what are the limits of what the bolt can do, specified at the level of the function of a bolt: how well can it hold things together in tension or shear), thread type and dimensions (i.e. will this bolt fit with other parts in the particular application), what standards does it adhere to (are their accepted industries that approve of the use of this bolt?).

    These all have analogs in software design. Note that NONE of them try to enumerate all of the ways that the bolt could be used; that is left up to the user of the bolt. The closest the documentation might get is listing standards compliance, and all that really is saying is “these other guys of a list of acceptable uses for this bolt if it meets this set of criteria, and we certify that it meets those criteria”.

    As a parting thought, a lot of the “line by line” commenting that goes into code really is the author talking to themselves in the future. The comments don’t really mean much to anyone else, because they have to “get inside the author’s head” before they can be interpreted.

    1. But with well-written software the parameters are all visible – with a bolt they are largely invisible.
      For me, a well-written specification is by far the most important thing.
      For me, the only essential comments are when you take a shortcut, and want to improve the code later.

  14. If read_temperature and init_adc are small and nearby, then documenting them redundantly at get_temperature is bad. good code can explain itself, the trick to a good comment is to explain something that the code has not already explained better.

    1. If only these code comment guidelines, to avoid redundancy, to explain better than has already been written, also applied to the posting of comments on Hackaday articles.

    1. That explains all of the unedited videos with bad lighting, bad camera work, scriptless rambling that take minutes to convey ideas, when it could’ve been done in seconds by a few well-written paragraphs.

  15. Comments lie. I found one in our code base yesterday.

    I agree with the content of the article just not the headline. Comments describe the intent of the code (at the time, then someone forgot or a new sprint happened and that “I’ll do it later” becomes a bug). Code is fact. We shouldn’t give up clean code but we should also use comments. Comments are there to tell you the bits about the code that the code won’t tell you (easily). Business rules, edge cases, bugs, etc should all be in comments but people can write code that others can grok easily. So it’s a balancing act.

    1. Not only can comments lie, but code can too.

      Undefined behaviours (according to standard) catch many (all those boolean operations taught at school that simply are not true within the standards).

      Implementation defined behaviours, such as what to do when long is 32 bits but memory addressing is 64 bits (all those 4Gb limits..).

      And conceptual troubles: you can’t fit a 64bit address space, and a distinct NULL symbol, into a 64bit number (off by one). Some things we quietly ignore, others bite us badly.

  16. You get me thinking there. The major difference between code and comments is, that we today have compilers that tell us about plain wrong code and on higher level languages we even have IDEs that do that while we code. Not only for straight forward errors, but also hints for coding convention and warnings on ambiguous code.

    What we would need to improve commenting overall, would be proof checking for comments. Like a git warning on a commit if the accompanying comment wasn’t touched. You could pull that of with something like /# instead of /* for a ‘secured comment’.
    Or a way to link code parts and comments. To stay within the articles example: if the temperature range is 0…1000, have this value pulled into the comment dynamically.
    So what I think is, we need some universal code/markup for comments, that works both ways, not just like doxygen.

    1. Good point, and I like the idea of a pre-commit hook that would check if comments related to a changed code have changed. At least with Javadoc, and to an extent with Doxygen, you can link functions and values into the comments, so if for example a constant changes, the comments would change automatically along. Gets a bit more complex, but this could then be used to check if for example a function referenced in one comment has changed, and if so, if that comment itself was modified.

  17. class TemperatureReader : public Singleton
    {
    public:
    class Temperature()
    {
    public:
    Temperature(int tenthDegrees) : m_TenthDegrees(tenthDegrees) {}
    int TenthDegrees() const {return m_TenthDegrees;}

    private:
    int m_TenthDegrees;
    };

    TemperatureReader()
    {
    init_adc();
    }
    std::optional Temperature() const
    {
    // etc
    }
    };

      1. I was dead serious.

        The TemperatureReader class enforces that init_adc is called before you can even attempt to get the temperature. Making it a singleton ensures that init_adc is called exactly once.

        The Temperature() getter returns an Optional temperature object (the HAD comment system discarded my angle brackets; it meant to return a std::optional < Temerature >). This forces you to check for a null return value. Much better than returning -1 (thought about below zero temperatures?).

        The only way to get the temperature is through a single getter function:
        temp.TenthDegrees()
        so it’s obvious what you get.

        Zero overhead compared to the C version. Well ok, one 32 bit pointer for the singleton object.

        Good code definitely documents itself. At least you can come a very long way.

        1. It is only obvious what you get if you define the temperature scale you are using.

          Is that degrees Kelvin, degrees Celsius, degrees Fahrenheit, degrees Rankin degrees Reaumur or a custom scale?
          Not at all clear from the code where a single line of comment would make it clear.

          1. If it’s really important then you should just fix it with code and delete the comment. Instead of using a primitive datatype that could be anything, use a class instead. Literally use a class called Celsius. It’s not ambiguous and now you don’t need any comment.

  18. I would like to see a way to just make comments italic, and not in the usual coding mono-spaced font. I know, you can make your IDE or editor do whatever you want. But no. I want everyone to be able to see it the way I wrote it. Can that be so hard? All those //s and *s and white space are a pain and clutter up the pages — noise.

  19. im of the opinion that it is impossible to overdocument. im one of those horrible people who likes to comment almost every line. mostly so that when i come back to in years later i know what the hell i wrote. its more to make up for my terrible memory than to pass my knowledge on to others. if im using some bizzare algorithm, i will put in more comments than code just because i don’t want to have to research the problem again.

  20. Of course this kind of article that takes one side of a well known controversial topic is certain to yield a record setting list of comments, but surely that is what the writer intended?

    My advice is to comment for yourself. Comment large blocks. Don’t comment what a competent programmer can figure out for themselves reading the code. Comment what isn’t obvious and what will trip you up later. Don’t add a comment like “initialize counter” to the statement count=0; — unless of course you are entirely daft.

  21. Comments make your code more maintainable. Everyone who has maintained code probably knows this scenario:

    You need to make a change, but first you need to know exactly what the code does. So you read through the function you think you need to update, and it calls do_something (foo, bar, baz) [which is uncommented], so you read through do_something() to figure out what it does, and it calls do_something_else (wibble, quux) [which as also uncommented], so you start reading that code, and before you know it you’re seven levels deep still reading code to figure out WTF do_something() actually does.

    This is why I mandate interface contracts in my coding style documents. Stated simply:
    “The function comment, plus the function and parameter names, should tell you everything a caller needs to know to use the function – you should not have to read the code to figure out how to do this. Comments that explain implementation details should go inside the function.”

    So well-named trivial functions with meaningful parameter names might need no comments.

    More complex functions should have function comments that detail what the function does, any side effects, plus any pre and post requisites if applicable, plus each parameter should be commented – in particular noting any special cases in their use [such as strings that can optionally be NULL, integers that might have special ‘ignore me’ values etc, the return value [if used] should also be commented, in particular specifying any error codes.

    And yeah, comments can get out of date – that’s one of the things you should pick up in code reviews.

    I have encountered documentation that said one thing, a comment that said a different thing, and code that did something else again… that’s why we have a ‘fix commenting, even if it wasn’t you that broke it’ policy when maintaining code.

  22. When i see your comments i typically just delete them. You’re welcome. The realm of “why” is contained almost completely within your unit tests. Your edge cases, use cases, and failure scenarios are all contained within your unit tests. If you put those useless comments back in then i’m going to revoke your source control access. Have a good day.

      1. I have a Sr. Software Engineer position open, if you’re interested. Not far from Louisville, Ky. The dress code is officially two words: dress appropriately. Projects are delivered to dev with a visual prototype as well as written user stories with acceptance criteria. Near zero red tape and office garbage. We also don’t play blame games. We would want your criticisms to be more constructive or descriptive though. For example: “this code sucks” is bad. “this code sucks because it’s 5 nested ternaries on one line” is good.

        1. The code with no comments sucks, period. End of story. No need to go any deeper.

          There is no way a code can be self-documenting. There is no way unit tests can convey the “why” part of the story.

          You cannot put there all the things you *dismissed*, all the *implicit* assumptions you have about the data (good luck encoding them *all* in a formal way). Same for the integration tests.

          And what kind of code does not suck? I suggest that you look up what “Literate Programming” is. And please, never again read anything written by Uncle Bob, he’s an idiot.

  23. Upvote.

    I recently came across some code that I was involved in over 30 years ago … funnily enough it’s great to read through it due to the comments that we were were forced to add in the “code inspection” part of the development process. I still remember the way that the older (and actually better) guys forced we kids to comment code properly. Putting good comments in code is not tedious or boring, it’s part of doing a good job.

    IMHO, it’s only the truly arrogant, uneducated or truly careless who advocate the “code documents itself” style. They also tend to produce what they think is “write only” code where they don’t have to read it again.

  24. “And yes, that includes private methods and static code in C. It’s such a major misconception to claim that they contain irrelevant implementation details that require no documentation, or are anyway not exposed to the “consumers” of the code. Well, at least the latter part might be true if we consider the users of libraries, APIs, and the like….”

    Is your open-source code closed-source?

  25. My favorite type of comments are the bird’s-eye-view comments rather than the this-function-does-this comments. Knowing how the data flows, when the interrupts are supposed to fire, and where the edges that the code is supposed to interact with are super helpful.

    1. We called that the ‘theory of operation’ in the Marines, and every service manual had that, along with a complete schematic of the device.

      Documentation is vital. Without it, you can’t fix it, whether it’s hardware or software.

  26. ” we need to face some harsh truths and start admitting that there is no such thing as self-documenting code”: Nope, I have had to maintain a lot of self documenting code in my life and been very thankfull for it. Also, I prefer reading the actual code when debugging software I did not write rather than trusting comments: trust but verify. I only need explanation when reading the code would take too much time but this usually means bad code.

  27. For programing, I’m trying to get this mantra to catch on. “Follow the data.” I’ve found it to be an incredible debugging tool. Usually a bug appears because some piece of data is wrong. Why? Was there a logic error? Bug found. Was the logic error due to another piece of data? Follow the data back.

    The same idea can work for documenting code. The get_temperature() example is good because that is exactly what you are documenting, the data. Or rather, you are documenting how the function interacts with data at a more detailed level.

  28. Here is another way to think about all of this. Code is just another language. As a programmer, at some point you need to become fluent in the language. Using comments to explain things that can be conveyed best by the code is stupid. Code is interesting because it can serve a dual purpose. It can communicate to other humans if written well, and it can tell the machine what to do. A good programmer strives to write code that will communicate effectively with other programmers as well as telling the machine what to do.

    Comments should compliment the code. They should express things that cannot be expressed in this other language we call “code”. The nut of the whole thing is figuring out which does what best.

    ———–
    And I just have to grind an axe on one thing. Names for throw away loop variables. Always just call them “i” and “j” for crying out loud. Nothing is gained by inventing a clever name for the local variable used to initialize an array. In fact it will tend to waste peoples time because they will suspect it may be some global used elsewhere (why else would you not have just called it “i”?) or have some deeper meaning.

  29. Documentation is NOT commenting. Commenting is NOT Documenting.

    1. Refactor until you get to the Quality Plateau (see the Programmer’s Stone).
    2. Even accurately commented spaghetti code is still spaghetti code. Not pastable to understand or modify.
    3. Think hardware – you need a theory of operation EXTERNAL document instead of trying to put it in the schematic or layout.
    4. Comments should indicate an exception, e.g. “simpler sorting algorithm because the dataset is small”, or “Compiler will optimize out a necessary step if altered”. Comments should not be data but meta-data.
    5. The Badgile ticket history is no substitute for clear requirements and design – it cannot replace them, only create a series of increasingly obscure patches, especially if refactoring is not done aggressively.
    (Agile has its place, but often it is brought in as snake oil so designers and architects can disappear, not unlike C++ and OO was supposed to fix everything and turn the mathematically challenged into top programmers a decade or two ago – OO has its place too but also can’t fix stupid).

  30. I can remember creating a program more than thirty years ago that would pull variable & function names and associated specially formatted comments from code that could be printed and sit beside me as a quick reference to what everything did. That was absolutely priceless when it came to both writing and maintaining said code, and the text file that was created was simple to edit and fill out to create documentation. Unless you are writing short scripts to do a single task, there is no reason not to do that, even nowadays.

    1. I forget the details, but I remember being in a meeting years ago about whether to spend the time to make some change. I got tired of listening to that and left. I was done making the change by the time they were done yapping.

  31. “Make sure to call init_adc() before calling this function here, or you will get undefined data”.
    Really? you suggest to do write better comments instead of fixing code horrors? of course if you show bad code example, it makes sense to add comments but you have to see it the other way round: learn how to code and then you’ll see you dont need comments. Try modifying your example with something like if(!is_adc_initialized) init_adc and managing the -1 error in a better way; also info about the range return is completely unnecessary or must also be managed in different way: what if the caller of the function relay on the fact that max return value is 1000 and then you change it to 2000?
    Comments dont solve the problem of bad programmers neither they alleviate it, so they are useless!!

    1. It’s a code example that forces the need for comments. But like you suggested, each comment is a bullet point on a todo list to improve the function. Each comment is pointing to a programming failure. The initializer should be handled better. In OO it would be when the object is first created. Easy fix in the constructor and then delete the comment. The temperature range values should be public read-only values on the object, MinTemperatureValue, MaxTemperatureValue. Easy fix and then delete that comment. I’m not going to approach the error handling on here because it can be a controversial subject. But if returning -1 is the error handling pattern for that project then it doesn’t need to be documented for every function. The read_temperature() comment is a huge failure, imo. How is get_temp and read_temp different? you can’t tell from the name so you’d have to check the code. get_temperature() should be renamed to get_last_read_temperature(). Now you can tell the funcs apart and the names better describe what they are doing.

    2. Fair enough, but just to clarify, the temperature example wasn’t meant as “the one true and only way to do it”, but as a random scenario where details not obvious from the code itself would be worth mentioning. Whether they should be there in the first place is a different story, and you are right that a better implementation could just take care of the initialization itself, but there could be dozens of reasons why this wouldn’t be possible in another system. Software is simply complex and rarely straightforward with one single, universally valid way to get things done – not even a Hello World.

  32. I consider programming to be a literary art, and my source codes reflect that attitude — they are attractive to the eye, easily readable, and are well-organized and consistent in style. In practice, I put the great majority of comments into my C++ header files, in doxygen format. These header files are the high-level design of the module/class/program. As such, they are part of the INTERFACE, and changing the interface to a module/class/program obviously has global implications. Comments or names describe what a module/class/program does, implements, or means, and the same for every function, argument, return value, and corner case — i.e. every aspect of the interface. Keeping these comments up-to-date is part and parcel of modifying the interface to the module/class/program. My programs are usually physics, and specifying units is an essential part of the comments, unless the entire program (or toolkit) has a global set of units. Bottom line: I rarely need a debugger, as the code is well laid out and easy to navigate and understand. Typos the compiler accepts are the biggest source of bugs; testing usually takes more thought and more time than coding.

  33. “Self-documenting code doesn’t exist” is a myth bad programmers tell themselves, as well as “if I write lots of comments it will make up for my bad code”.

    When I see scads of comments throughout code, I inwardly groan, because I know I will be debugging someone else’s buggy comments as well as their buggy code. I’ve been doing this professionally over 30 years, comments are generally worthless.

    When I am debugging someone else’s code, I don’t care what they THINK it does, because they got it wrong. I need to know what the code ACTUALLY does, and the code tells me that. Lots of comments are definitely a code smell, they tell me the author wasn’t really sure of what they were doing.

    The example in the article can be easily re-written to remove the need for the comments, (as eloquently showed in the comment above). I don’t think I’ve ever seen a genuine example of “you really need a comment here” code.

  34. There are situations where a precise comment can save me days or weeks of struggle. For example, when I debug work done by others …
    As an exercise, I do (I did before being blacklisted) ask my colleagues to read code they wrote over a year ago. See if you remember what each line does :)

  35. Ironically, the comments on this article are a good read!

    No one has yet mentioned that high-level languages, from C to SQL, were designed exclusively for humans to read and write. They are human languages, not computer languages, specialized for a certain class of problems just as scientific jargon is specialized for its own problems. Translating the clarity of some of these explanations into a language as muddled as English can be counterproductive.

    It’s rare that I’ve seen a comment and thought, “That’s helpful—I’m glad I read that.” Even when it was something I myself had written, I usually look at the comments and say, “That’s misleading as hell,” and remove them. They may have seemed like good explanations at the time, but I don’t remember enough of what I was thinking for the English to make sense months or years later. The Python or C++, on the other hand, can bridge the span of time.

    1. “Translating the clarity of some of these explanations into a language as muddled as English can be counterproductive.”

      Therefore, I propose that all future comments be written in Latin.

  36. I think that you can get rid of 90% of comments with proper variable/function naming and types/interfaces (if it’s a statically typed language). Also, by using short, named functions for sub-steps instead of endless imperative and/or spaghetti code.

    Takes more work than just spitting out comments but this is what makes the code as ‘self-documenting’ as possible. Then you can leave some comments to describe non-trivial steps in logic for a future reader. If you have unit tests, you can use that to document and keep an eye on edge cases.

    What absolutely drives me nuts is code that is commented out ‘temporarily’. My colleagues are more experienced and maybe even better programmers than me, but I can’t convince them not to do this.

    I argue that there is the ‘file history’ function in Git to retrieve old code.
    Their argument is that ‘it’s better to have it there so that they remember to maybe uncomment it later’.

    Result: commented out code sitting there for >1 year, I don’t know what it did, why it was commented out, it litters my search results when refactoring, and it probably broke a week after being commented out.

    I’d just delete all that crap if they weren’t my bosses.

    Maybe I will do it anyway and see if they ever notice.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.