On Comments in Code
henrikwarne.com
henrikwarne.com
In other words not just the "why" but the "why not".
Many times I've gone back to code I'd written previously, it seemed overly complicated, I replaced it with a simpler version... that failed... that reminded me that was what I'd tried originally and then replaced it with the more complicated version for a good reason.
So now I have a new rule: every time I try a simple approach and it fails and I replace it with a more complicated one that works, I add a comment explaining the previous strategy and why it didn't work.
Hilariously, some of these have grown to several failed attempts. "Tried A library but it has a critical bug where B happens. Then tried C function but it also has a bug where D happens. Using external command E doesn't work because F..."
But hey -- neither I nor anyone else will try to reprogram something simpler. Or we can check if the library mentioned still has the bug in question.
Another benifit I have found is that in studying the problem to understand why the simpler approach doesn't work, you find out that it can actually work.
I'm sure it would be possible to build such a tool, but the complexity is quite substantial compared to a simple comment or two. If you really want to go the full-test-coverage route it makes a lot more sense to focus that energy on pure/immutable/statically-verified programming rather than some finicky test cases that add substantial maintenance cost to the code.
1. Ancestral memory of programming in languages like assembly or early Fortran where your code was going to be spaghetti because the language was capable of little else, so it had to be drenched in comments that would be unnecessary in a better language.
2. Overreaction to personal memory of overzealous professors who demanded assignments be drenched in excessive comments.
1) The comments are correct, but the code is wrong. This is a bug. 2) The comments are wrong, but the code is correct. This is equally a bug. Whoever makes the change has an obligation to describe their changes with a comment.
Code with no comments? Could be right, could be wrong, who knows?
That's a not-often-talked-about (that I see anyway?) thing I really like about rust - I don't get to use it much, but when I do I think having 'doc tests' alongside comments heading a function and 'module tests' at the bottom of the same file is great.
10 or 20 years from now your code may still be running, the issue system or the git commit log may not be.
Do you always read the whole git history of a file before editing it? What interface do you use for that?
I still wouldn't use git as a replacement for comments since git history is wiped out all the time. Moving files or reformatting or a million other things could remove a critical commit message.
In the context of regular development? I almost never do any of that.
A practical reason for longer-form commit messages even in modern PR workflows is that they allow you to create and view commentary on a group of changes across files without leaving your editor.
As an interface to git, nothing beats magit. Even if you never use Emacs for editing any text files, it's useful as a magit runner.
Moving/renaming a file? Commit the deletion and insertion together, preferably with no other changes (including to the contents of the file; commit those afterwards). In the best case, VC will treat this as a move and showing the history will include that of the old place/name. In the worst case, that history of file 'foo' will "end" with a commit like 'Rename bar to foo', with a diff showing the content moving from 'bar' to 'foo'; we can continue reading the history by switching to 'bar' if we want.
Moving code from one file to another? Similar story: commit the insertion and removal together; preferably with little/nothing else in that commit; preferably with a commit message explaining the change. This way, the code doesn't appear from nowhere in the commit history: when we look at the commit where it appears, we will also see where it came from before.
Moving a file from one repo to another? Dump that file's history as a patch, apply that patch in the new repo, then clean up (e.g. rename, move, etc. as appropriate). Alternatively, for larger sets of changes, add the old repo as a remote for the new repo, make a branch in the old repo which contains only those parts we want to move, fetch that branch into the new repo and merge it, then remove the remote.
I don’t disagree with you but you can’t prevent it.
Having worked with codebases that have switched SCMs and SCM-adjacent issue management systems multiple times without retaining full history, I'd prefer to have the commentary in code even if it is also in those other places.
If I come up with an inefficient algorithm that's short and readable, I tend to optimise it to something better but leave the reference implementation inside a code block.
(This goes without saying, but: I don't leave "commented code", but code inside comment, generally formatted with Markdown)
Save that poor sod; have your comments live in your code, not in an SCM system for your code.
Rather than adding a comment saying "hey make sure you change that other section" I have test which lists the columns in the table and fails if they change. Then the person who changed it will see the comment on the test explaining why it failed and what they must do.
// Dear maintainer:
//
// Once you are done trying to 'optimize' this routine,
// and have realized what a terrible mistake that was,
// please increment the following counter as a warning
// to the next guy:
//
// total_hours_wasted_here = 42
//
This place is a message… and part of a system of messages… pay attention to it!Sending this message was important to us. We considered ourselves to be a powerful culture.
This place is not a place of honor…no highly esteemed deed is commemorated here… nothing valued is here.
What is here is dangerous and repulsive to us. This message is a warning about danger.
The danger is in a particular location… it increases toward a center… the center of danger is here… of a particular size and shape, and below us.
// Dear maintainer,
// The code which follows was hacked together under desperate pressure.
// It's not smart. // It's not doing anything really subtle. // It's the only thing we could get to work in the time.
// If you want to chuck it and replace it, you absolutely should.
I'm doing that atm, but I think my predecessor lacked the humility to admit that his code was bad, which is how it went up to 150K of code that basically concatenates XML, converts it to JSON and concatenates HTML on the front-end from the result.
>So now I have a new rule: every time I try a simple approach and it fails and I replace it with a more complicated one that works, I add a comment explaining the previous strategy and why it didn't work.
My strategy is to comment out the failed attempt and leave it there for next time.
> Resignation letter sent
Sometimes code is the only way to love and to talk with...
Edited: fix some typo errors
Codes needs "why" comments, not "what" comments. The "what" can be done by self-documenting code, _most_ of the time. You still need to write "what" comments sometimes, don't rule it out completely. And you routinely need to write "why" comments, self-documenting code will never provide the context of "why".
Write more "why" comments.
Indeed. While there are exceptions, "what" comments will usually only add noise thus making things more difficult.
This isn't necessary:
// Total count
int total_count = 0;But then the state being read and mutated by a block of code is explicit. The alternative is having to inspect the code to determine if and what state is being manipulated. The more code in a functional unit, the harder this gets.
A "why" comment usually ages well, so I generally trust it and can use it to better parse the code and its intentions.
So I belong in the "what only for API methods" camp.
// Reset the counter for the next run
total_count = 0; // reuse counter for performance, this brings 2% speedup
total_count = 0;
would be a quite useful why comment, if not creating a new variable each time is intentionalI would need to see a realistic example to believe that something like this would ever be a useful comment.
The story is different, if the variable is captured by a closure/lambda, the current stack frame or part of it may be allocated on heap, leading to perf issues.
Is there somewhere that's documented for LLVM? That's my expectation of what the final compiler output should be, but I read that the IR allocates every variable dynamically, and then they optimize away whatever they can. I haven't been able to figure out how or where it's guaranteed that all allocations will be coalesced.
I wouldn't normally worry about it, but I've talked to folks who don't believe me when I say it doesn't matter whether you declare an integer inside or outside of a loop. It would be nice to be able to explain exactly why it can never matter.
By example, with https://godbolt.org/z/9Kv7oo9oK you can see that values goes into registers (and that thank to 'lea' there is not many registers used).
And if you remove the option -O2, values are spilled on stack.
The contention is whether it is necessary or common enough to be assumed. In most places this would be self evident and should be avoided to reduce mental load, however I have seen and done exactly this many times - in distrubuted or embedded systems where the control is neither synchronous or co-located (ie, coming in from interrupts / messages). In such places there may even be call for more description, however presuming the context is appropriate this could be correct.
I would imagine a game with a react redux style state engine might result in a comment like this as well.
Yes, "what" comments are a source of additional developer effort and of possible inconsistencies between code and comments, but good API documentation is worth writing and maintaining.
No matter how well you name your method and its arguments, if it's part of the public API and you don't include a comprehensive documentation comment, I'll be forced to dig through your source.
This is the thing that annoys me with arguments about all this. I will generally say "document the 'why' and not the 'what'; if the 'what' isn't clear from the code, fix the code to make it clear". And people will say "that's invalid because sometimes you really do need to do something clever and the 'what' needs to be documented" or whatever. And yes, that's fine! Stop taking everything people say as if it's an absolute, 100%-of-the-time statement! Obviously there are exceptions. That doesn't make the common-case rule useless or uninteresting. People should assume "most of the time" is the default implication about these sorts of "rules" unless otherwise noted.
double compute_angular_acceleration_in_radians_per_second_per_second(double torque_in_newtons_per_meter, double moment_of_inertia_in_kilogram_meters_meters);
vs
// Units are base unitsSI.
double compute_angular_accel(double tau, double J);
I'll take the second any day.
I find that you do sometimes need symbol names that long even though it's a code smell indicating something else is up - usually scopes that are too large or insufficiently expressive types.
I view excessively long variable names as a midpoint on a journey to improving code quality on really bad code bases.
In my opinion any code that deals with physical quantities, but which does not use dedicated types for each kind of physical quantity, where the type definitions include the units used, is erroneous.
Any mistake like in the Mars orbiter must be detected at compile time.
* It's not your code - I was working with some UNIX TTY code recently and the APIs are horrendous 1970s throwbacks. But they're not changing. Working with crappy APIs is where comments about the "what" shine the most. You can only go so far with abstraction/encapsulation/facades.
* You don't have time - there's an urgent bugfix you need to get out yesterday. Do you A) rewrite the architecture to make it perfectly understandable and tell the users to wait B) drop a comment in explaining what's going on.
Yep, that'll happen, and when it does, you document why you documented the 'what'! :P
This kind of lazy black-and-white thinking is what 'consistency is the hobgoblin of small minds' is talking about. The exception proves the rule (...most of the time...)
A ticket is a really opaque way of showing something. If you click the issue you're now going through several tangentially related comments and a few MR back and forths just to work out if its even useful to you. People will stop bothering to even open the link unless they are really stuck.
Whack a summary comment and then by all means include the link, but the comment is what most people will use.
[1]https://replit.com/@bramses/stenography-carbon-bot#index.js
When reading their code I'll add feedback to parts I don't understand (why do we need this cache invalidation, why do we use a worker pool here instead of throttling requests etc).
I find that questioning sits in the back of my mind when I write code - almost like a pair coder - to the point that I try to preempt questions like this by adding it as a comment.
Similarly for anything that's publicly accessible like public functions/properties/interfaces/classes, my goal for codedoc comments is to prevent the consumer (me or my team) from having to open the code to see what it does and how to consume it. If they can use it with just intellisense then it's a win.
Just like code readability and design, I'm seeing comments as another tool to communicate intent that can't always be communicated by code alone.
I couldn't disagree more.
I was recently programming a library where some parameters could be 0 or greater, some parameters necessarily greater than 0, some parameters could be Infinity, others couldn't...
Similarly, if one parameter is set to zero than another parameter will have no effect...
JSDoc kept the whole thing sane. "Reading the code" would take several minutes to figure out the answer in each case, which would be wasted time in my book.
JSDoc is awesome. Not every function needs it, but plenty do.
I wouldn't say this makes the comment useless, but it does reduce the usefulness. And this might end up biting someone who has decided to trust the comments.
Unfortunatly it is a story, that is set to repeat 3 times.
There is a similar tragedy of outdated documentation.
And then the final part of the trilogy is named "outdated and even misleading naming" (vars/functions).
In all cases, one should have same amount of trust as with weather forcast or politician's speech. Trusting 100% would be naive, but complete dismisall would be foolish as well.
It's one of those things that sounds nice, but once you've moved beyond a certain level of complexity you realize how impractical it is. The fact is that "good code" is often in the eye of the beholder and not everyone has the same skill or vision, so they might as well write a comment about what something does and the intent behind it rather than leave others guessing.
Either we admit we'll never budget maintaining the comments metadata on top of the code, and embrace that risk for the cost reduction it provides, or we do document for real and maintain and review and fix regularly at a cost.
But saying we dont need to do it because it s already done is a quick stunt that everyone saying it knows is dishonest.
I've been programming in one form or another since I was 8 years old (around 38 years) and this was obviously incorrect to me back then, even though I wasn't sharing my work with anyone else, and hadn't heard this concept.
Years later I was astounded to move into a "modern" dev environment where people (developers, devops) parroted this line regularly. As you say it's nice in principle, but in reality relying on it proves to be impractical.
> These comments may be useful for API:s exposed externally, but in an application where you have access to all the source code, they are mostly useless.
I used to love the eslint-enforced jsdoc in a repo I maintain, but since we embarked on a TS migration, it has become painfully redundant in 90%+ of cases (there are cases where the jsdoc contains extra commentary/context that TS doesn't cover, but I think those cases are covered by the author's other comment types).
So given the above, I think it's notable that you are referring to JSDoc (and by extension JS) whereas the author is referring to Javadoc (and by extension Java). The language choice is particularly important here.
The author mentions for preconditions to just “read the code”. I consider this bad advice. If using an external library, would you rather hover over the method and see its conditions, or would you rather crawl into the third party source code?
I recommend that you treat the internal structures of your code as reusable third party libraries, and not assume that anyone will be familiar with it or how it’s used.
Often my JSDoc comments take up more vertical space than the code itself, sometimes even with ASCII tables or example usage code. I believe this is one of the best approaches to documentation, especially paired with a automated documentation site generator tool.
Code is read much more than it is written. You need to think like a writer and consider your audience.
getFoo() does not need a comment.
if it does, (it shouldn't) and its JavaScript, JSDoc is a fine format.
if getFoo() does need a comment, consider changing the code so it doesn't.
Code is read much more often than its written: so be concise.
If the docs can be automated by a simple tool, by definition, they were not necessary.
If I have to read every line of implementation to know wtf is going on, then I'm probably going to have a bad time.
Take this real example:
/**
* change the temperature set point in use by the thermostat
*
* @param ctx the thermostat context
* @param sp the new set point in 0.1 celsius degrees
* @return 0 on success, < 0 in case of an error
*/
int change_sp(void *ctx, int sp);
But we can change around the name of the parameters like this and use proper strict types: /**
* change the temperature set point in use by the thermostat
*
* @param thermostat_ctx the thermostat context
* @param set_point the new set point
* @return SUCCESS on success, otherwise an error code
*/
error_t update_temperature_set_point(thermostat_ctx_t *thermostat_ctx, celsius_degree_t set_point);
And thus the doc comment now it's useless and can be removed, leaving only: thermostat_error_t update_temperature_set_point(thermostat_ctx_t \*thermostat_ctx, celsius_degree_t set_point);
And in a codebase, more than 95% of the comments would be like that. There are the exception where you need to explain something in more details. In that case you first should ask yourself if there is really not a better way, and if not in that case the doc comment is fine. In all the other cases, it's probably not.Doc comments on the other hand really makes the code less readable, the increase the number of lines for most of the times not saying anything useful at all.
I think one of the major motivators for the original style of code are legacy pressures and a concern about code width.
If every other function that manipulates temperature set points takes "int sp", then you're going to get a lot of pressure for consistency. A lot of programmers regard consistency as a goal in and of itself, and it's very easy to demand it during a code review. However, the demand for consistency prevents us from finding a new consistent target without excessive amounts of work. It may be better to reach a new consistency within a narrower scope, as long as a consensus has been reached to extended that consistency outwards. In this system, consistency should be viewed as a compromisable target: something that makes you ask a careful question. And when asking a question, take into consideration the propensity of the code author to view a question as a demand. If you're a senior and the code author is a junior, presume that means: ask a genuine question vocally; write down minutes of your discussion.
The code width concern is a bit silly, but for some reason we assume that things get weird when lines exceed 80 characters. That's easy to do.
Sometimes. Other times proper strict types aren't possible because the language doesn't support them (many scripting languages, C, etc.)
Assuming this is C, even a type like "celsius_degree_t" tells me nothing about the valid range. Am I supposed to just try it out and see what error code I get or are we back to looking at the implementation? In the case of C this will most likely involve locating and opening a completely different file whose name doesn't even have to be related to the header file.
The comment also doesn't need to be about all parameters and can contain additional information that's non-trivial to determine: is the function thread-safe, are there performance implications, is there notable resource usage, and so on.
Python in particular is notorious for having optional (keyword-) arguments and polymorphic arguments. Proper naming schemes are impossible in this case (lest you accept monstrosities like "node_as_id_or_name_or_object" as proper argument names) and type annotations have only very recently (as of 3.10) become somewhat sane. Type annotations don't help with kwargs, though.
TL;DR it greatly depends on the programming language and its capabilities whether naming and types alone can replace comments.
The problem is that not everyone code under the same circumstance, even inside the same codebase: you have the new joiner that has to reverse engineer most of what he uses, the senior who did or touched enough that he doesnt even see the color code for comments, the rushing helper who need to fix a bug now or else and cant maintain comments or he'll miss the deadline, etc.
Comments are sacrified first, tests seconds, code clarity third, correctness in edge cases next, etc. Embracing the reality that we're going to have to deal with crap once in a while would solve a lot of frustration.
You are documenting _what_. To document _why_, you must explain why somebody would want to change the "set point" and what is the effect, and not in terms that "setA sets a", but the semantics of it. What does setting point accomplish? What are the circumstances when you want to do it? _WHY_ would somebody want to call this method? Make the room colder? Make the room hotter? That kind of thing.
In this case, "set point" is a well-understood term, and its purpose is similarly well-understood. [1] Although the team may wish to document it, the place to do so is in the team glossary (or other overview documentation), not in every function that uses it.
[1] http://faculty.washington.edu/brengelm/neut_zone/pg3.html
Anyway, the point I am driving is, it's more interesting to discuss _why_ would anyone change the "set point" - common scenarios and pitfalls, than just explaining the "update_set_point changes the set point".
It's nice to go into details what are the consequences and common use-cases.
The reason I say that is because the language features (or lack thereof) are a big contributor to the need for JSDoc/Javadoc.
i.e. JSDoc adds much more to Javascript than Javadoc adds to Java.
For example, there is much less need for JSDoc in a Typescript project.
This works great in codebases that are designed to be read in that way, which is why I'm so keen on every commit combining tests, implementation, updated documentation AND a link to the associated issue thread.
I'll sometimes open an issue seconds before I make a commit, just so I can have an issue number I can associate the commit with. This is great for adding commentary later on - I might post a comment on an issue thread a year after the commit that help clarify some useful detail that, with hindsight, I should have recorded.
Commit messages are updated with the code automatically. Comments seem to become out of date almost immediately.
For example, a standard 5 year old React codebase probably used to contain a lot of class-based components and now is probably switching to function-based components with hooks. I know the first time I did a code review on my colleague's code that introduced some weird hook concept - I asked for some more comments to explain what magic was going on. I would probably not ask for such a comment today now that everyone is more or less familiar with hooks and function-based components.
More than any other approach to coding (x-based-development etc), this has come up most frequently for me personally, and it astounds me how many people have this mentality.
Comments are a way to break out of whatever terse syntax your given language requires and speak directly to the developer. A single comment can house so much more context and insight the best-formatted code could ever hope for. When the only downside is some holier-than-thou idea of "I shouldn't be doing this" (despite the fact you clearly need to), I'm surprised so many people fall for this terrible mentality.
- javadocs: at the top of the function. Explains what you need to expect from it, very useful when autocompleting (yes, you can go read the code...but that's slow)
- block description: before a bunch of lines. Explains what they do without need to read and understand them all, so you can read them faster.
- line description: at the right of a line of code. Explains the use of a specific variable, the reason you used a function call, etc.
- flow descriptor: the line after an if, an else and other path divisions like while or for. Explains why the code took that path. Useful when you need to understand the context of a specific line.
> If you wonder what the method does, or what the valid input range for a parameter is, you are better off just reading the code to see what it does.
I feel that this is a very inefficient approach to coding. If you tell me what the function does, what its valid inputs are, and what it returns then I don't need to look at the code at all.
More so when I'm collaborating with people outside my area of expertise: a colleague of mine wrote a function to "convert molecule SMILES into their neutralized form". What does it do? Beats me, I'm not a chemist. But thanks to the comments I don't need to know, and I'm grateful for that.
The problem is that someone on your team drastically changes existing methods behavior instead of writing new ones, and by doing that, is changing the behavior every historical caller expected.
I really think that if your changes are so important that they need the doc to be updated, it’s probably that you should write a brand new method. Changes in an already called method should only concern implementation details.
It is about people. In the world where team members last ~2 years and move on, expecting that documentation is left not updated is in my opinion perfectly valid assumption.
So you are usually better of reading the code anyway because you cannot trust that some dev updating code updated javadoc as well.
When something goes wrong I usually have to do is to dig into GIT history and see when and what changes were connected.
The most reliable Javadoc is @author, git tells you the author, @author tells you who the code was copy/pasted from.
Names do become misleading. Of course making callstack deep enough, will hide that.
Code reviews have same power to rectify both: naming and comment issues.
Great point.. except for when you're dealing with a lead and/or reviewer that refuses to approve comments because "we write self-documenting code" yikes
Of course it is possible to intentionally create a harmful comment, but if you trying to write a useful comment it is likely will be useful for most readers. In my career there was a lot of situations when a small comment would have saved me time - I've spend many minutes and even hours to discover something a code author knew but was too lazy to put into comments. I don't remember a single case when I suffered from an outdated/incorrect comment (though in this discussion such situations are mentioned).
I always hated that sentiment of "no comments", although I share the wish of computers understanding our code better. I think the refactoring tools didn't really live up to the promise, because they require the discipline of writing everything in the code in computer-readable format, otherwise they will leave fragments of wrong documentation in their wake. One of the benefits of human language is that you can easily create a DSL (concepts and terminology), and the refactoring tools usually cannot handle custom DSLs (much less vague ones) very well (seems like a strong AI problem).
This neatly groups the comment with the code it applies to.
Occasionally I have to make a function "just" for this grouping purpose, but that's fine.
And stripping comments before working on it is a thing I have done many times, on code I wasn't familiar with.
The biggest problem is lies. If you don't update your comments, don't write them. If you know someone less careful than you is going to take over and not update your comments, don't write them either.
And then, there are the redundant comments, the ones I see most often. For example
- Don't describe the function both in the header and source code, it is a useless copy-paste that will never be updated correctly. (mostly for C/C++)
- I know the syntax for declaring a constructor, thank you, you don't need to tell me that is is a constructor in the comments. And I can also guess that getX() gives me the value of X, no need to fill my screen with dozens of useless lines of comment.
- I don't need a comment to know who did what. We are under source control and we have a "blame" command.
- Don't use comments to disable code. Just delete it, it is not lost, we have history, and we are unlikely to need it anyways.
But the one that makes me rage the most is something like "int time; // the time". Not only it is useless, but you are not giving the info I want: the fucking unit! I've seen it way too often, sometimes with far from obvious units, like tens of microseconds. So if you want to put a comment, at least tell us the unit. Or better yet, don't comment anything and make your variable something like time_in_ms, or define a type.
I was bitten many times thinking "yes, this is the equation (21) of the well-known paper, no needs to comment" to then fall flat on the nose because this was well-known to me, not the other engineers coming from another field, an assumption was there for let say a concentration of a chemical in the formula but then it was used in another context where the assumption does not make sense, etc.
For all the "bit-pushing" part of the software, opening files, reading data and so on, it is way easier to have self documenting code.
A somewhat random example is https://github.com/ogham/rust-ansi-term/blob/master/src/styl...
Then under each one-line comment I'd write the code that does what's written.
Documentation, in general, could use all the help it can get.
I wrote up a long piece on this[0]. No one will read it, because it's long. I've found that no one reads anything that is more than about a "7 minute read," these days. Part of my documentation problem, is that I can get too verbose. It's not a good thing.
[0] https://littlegreenviper.com/miscellany/leaving-a-legacy/
While posting on HN I believe that your posts have good signal to noise ratios, so I will now henceforth go through your post history looking for quality content. This post was one such event. Because I viewed your content like this, length went from a cost to a value. I read the entire article(okay, I skimmed the code sections).
Love the blog. It made my skills as a developer better. Not much to add, a benefit of the verbosity.
In mathematical terminology, the condition may depend on some lemma or theorem the programmer proved to themselves when writing the code. (I say “lemma” and “theorem”, but this applies to any kind of complex business logic, or the state of a realtime system, etc.) For a lemma (fancy name for a small theorem), the reasoning can go inline in the code, as a comment. If it’s a theorem, a more substantial thing you proved to yourself when writing the code, it might be better put in a separate document which a comment in the code should point to.
The reasoning you used to show the code works is better off written down for those who follow than forcing them to think through the same logic again. And the act of expressing the logic in a comment will tend to flush out errors in that logic. Those are both good reasons to spend the time writing such comments, unless you don’t care about technical debt.
As a special case, runtime assertions often need a brief comment to explain why they must be true.
I still don't understand https://gist.github.com/chowells79/996f2749b088d287937e3eff1... on the first read, even with the ridiculous overdocumenting. On the plus side, it's clear enough what it does, even though the details of "how" are hard to follow.
Still, if there ever was a case for documenting the "how" over the "why", that code is it. It's pretty easy to understand why that code exists. It's actually quite hard to follow the details of how it does it. Those comments are excessive, but they do cut the time it takes to rediscover the "how" whenever I get curious.
http://jeremymikkola.com/posts/2021_03_21_useful_comments.ht...
If you're calling the SaveUser function and passing in a new User object that you had just created, a comment of "Save the new User object" adds nothing but noise.
Although people have vastly different ideas of what is or isn't obvious.
So, code should be written in a way that is easy for a human to read and understand.
Code should easily and naturally show what it's intended to achieve 99% of the time.
Comments should be used sparsely, and only to express things that must be expressed and cannot be expressed in code.
Compiled machine code is for your computer.
If you were addressing a machine, you would be writing 0s and 1s, because o boy do the machine know how to execute those little ons & offs.
The point of the program is to get the computer to perform some task; the way we get the program into the computer is to write code.
You can say we're writing code for the compiler if you want to be pedantic; or you can say we're writing code for humans too, which is a perfectly valid point. But what do you actually mean by insisting that no, the code isn't for the computer at all, it's solely for other humans? It's clearly the language we use to communicate with the computer to tell it what to do; the fact that there's a translation step (performed by the computer) between what we write and what the computer executes doesn't change that in any relevant way.
By insisting on this, I mean that, at any given time, if I have the choice to write code that is easily readable by an human XOR a code that is easy to execute (fast), I will always choose the first.
And it’s not a dogmatic ideology : sure there are tradeoffs where writing efficient code is needed, sure it depends on the industry you work.
But in general, when needed, it’s much easier to make a readable code faster than to make a fast code easier to read.
I’ve seen so much « optimized for machine » code in my career. Probably written by really smart person who chased the millisecond perfection. But 90% of this code is « one shot execution » where millisecond doesn’t change anything and is probably wasted anyway by the surrounding framework/library/context…
I can write a basic function in 10 different ways that basically do the same, but have bugs or small differences in side effects that might not be obvious to a human reader at first glance. Languages just lack the expressiveness to explain those nuances, hence comments.
I save most of my remarks for commit messages, which are understood to be contemporaneous. This method requires the average commit message quality be high, otherwise, people are unlikely to think to run blame on the file, even though most text editors can do so effortlessly.
I used to be a firm believer in self documenting code, but that approach often led to a large number of functions that only get called once. So lately, I have been just putting a comment line where I otherwise would have refactored the code into a function, and I feel that it has made my code a lot easier to read, and understand.
Exactly. Rarely hear someone say there are too many comments in this code! And feel free to not stop at the docstrings. Nested loops, obfuscated 1 liners, business logic that required a cross functional meeting to understand, are all valid reasons to be generous with comments.
What happened to literate programming?
Back in the CoffeeScript days, it's creator adopted a literate programming format, which was basically Markdown with code blocks being CoffeeScript.
He talked about that as the future of programming. Whelp, CoffeeScript got killed off by ES2015 and the file format never caught on.
I know the concept of literate programming does come from Knuth, but I first heard about it with CoffeeScript.
Why did this never catch on?
Having read some of Knuth's literate code... good god that man is brilliant, but aesthetic language design isn't his wheelhouse
That said it has caught on some with data science and actually is an option with swift playgrounds.
I'm having trouble drawing up a distinct example.
// --- Init ---
// --- Mainloop --
// --- Cleanup ---After a while of doing this, I get a sense for what kind of stuff I'll find puzzling later and can comment preemptively.
Don't ever add SBO comments.
SBO = Stating the Bleeding Obvious.
Sometimes, the "obvious" code has a bug, but its intended purpose is no longer obvious. Commends for "obvious" things add redundancy, like error-correcting codes.
On other aspect, I can't believe this lived for 6 hours without the mandatory quote: https://xkcd.com/1421/
Enjoy.
I do it like this as I think that conceptually duplicating code logic through e.g. comments can be dangerously imprecise. E.g. when someone changes the code (and not the doublicating comment), there are no checks in place for this mismatch of code and comment to be caught by e.g. a test.
If I see a line that looks like it might be a bug, or an unnecessary duplication, how do I know if it's a mistake or not?