“My Code is Self-Documenting”
ericholscher.com
ericholscher.com
Indeed not everything can be expressed in methods and variable names etc and so comments can be helpful occasionally. But the focus must be on clearly written code with comments as an exception, not a rule.
As for SDC being myopic, I beg to differ. Code (and contents) is for developers and machines. End users and API consumers should have documents (hence documentation) available to them for purposes of tutorials, user guides and references.
All of that being said, I'm not dogmatic about any of the above. I'd prefer my code to be self-documenting, but I understand it isn't always for any number of reasons.
How am I supposed to know if the comments have really helped the code, or if is that the code was just not written in a self-documenting way to begin with? Or, form the other perspective, how am I to know if the self-documenting code isn't as self-documenting as the original author likes to think? Perhaps even more importantly, how will I learn to take on that better style without examples to study?
In a world where we have a treasure-trove of great open source software, you would think the first step would be to point to exemplar examples to show exactly what can be gained by writing code in the prescribed manner. The "I do it this way and it is great. Believe me.", unfortunately, does not make for a good discussion.
It sometimes happens with self-documenting code too, but only when someone refactors enough of the code so that the class or method no longer matches the original name.
but they usually are.
Just because business folks don't know how to value certain developer practices doesn't let you off the hook. Just do what you have to do and if it takes longer because you had to learn how to do it, then it takes longer.
Let the business folks tell you when it's taking too long to deliver features.
A manager looks at a programmer bringing up something like unit testing as "why the fuck is he asking me about this, it's his job? Obviously because he's asking me it's outside of his professional ken, so that means it's going to take absolutely forever." Out loud he'll just say, "Can you just get the feature done?"
Don't ask, just do it. It takes as long as it takes. If they ask, just say it's not done yet.
But for what it's worth, I agree with you in that I'm still on the hook. Either to help educate my manager, or to pick managers who already understand this stuff.
All they care about is that the code works, and that features can be delivered on time.
That second part "delivered on time" includes maintaining the comments along with the code since it makes the code more readable, and eases future changes. If Management doesn't give you time to write maintainable code (which includes keeping comments in sync with the code), then you may as well start looking for a new job because the technical debt is going to pile up and it's going to get harder and harder to meet deadlines.
I find that I have this problem with my own code. Ridiculous, but it happens.
In terms of developer resources, the act of doing updating the comments is cheap, but the act of making sure it gets done (in a systematic way) is comparatively expensive.
You could always use a tool like Danger (http://danger.systems/) to inspect your code and warn you when code has been updated, but comments nearby have not been.
This is what code coverage analysis/metrics try to address.
> You could always use a tool like Danger (http://danger.systems/) to inspect your code and warn you when code has been updated, but comments nearby have not been.
I'd worry about tuning out said warnings after too many false positives. Comments regular enough to not generate too many false positives can generally be turned into real syntax of some description.
For example: python functions shall have a docstring unless trivial. For external api functions on a class, there must be at least one example. etc etc. Plus, this makes working in ipython really nice!
First, other developers know that comments can grow stale, so they're unlikely to treat them as sacrosanct, although I admit to falling into that trap.
More importantly, most of the types of comments that Eric recommends can drift out of sync with the code and still be useful. Why is this code here? What happened previously that is no longer in the code? What tradeoffs have been chosen?
Yes, because they should be, and are, largely ignored. Of course, deleting them is better than ignoring and not having them in the first place is better than deleting.
Code doesn't depend on variable/function names either. Yet you don't argue they fall out of sync, you simply update them. And this should be done for comments, too.
This is not true of comments.
If we could make outdated comments produce compilation errors, we would live in a wonderful world :)
I really hate it when the compiler ignores my comments.
Doxygen supports documenting function parameters, C++ template parameters, return values then Doxygen and Clang can tell if the name listed for some of those things doesn't match the actual code or if a certain kinds of comments are missing. This won't tell you the documentation is wrong, but it would stop you from adding or removing a function parameters while ignoring documentation comments entirely.
If you want to go crazy you can write Clang plugins using LibTooling and run and code you want and analyze the comments any way you choose.
Depends what language you are working in; in some it won't fail until runtime.
> If we could make outdated comments produce compilation errors, we would live in a wonderful world
It is impossible. The whole point of having comments (and also identifiers) is to tie the code to things that are not formally described. If we would formally describe them, they would become the code.
You could, at best, mark identifiers in comments (and what their refer to), and then you could use this to hint at comments that need to be updated (as somebody else mentioned). But it will never be a perfect process, because there will always be a boundary between formal and informal.
Snarkily, how lazy* do you have to be to not even delete comments you make stale?
* And not as in the virtues of programming lazy.
The closer a piece of documentation is to the code the more likely it is to be updated. External manuals always become stale and comments in C headers are more likely to be stale than comments next to the implementation in C source files (or in any language that doesn't even have that distinction). Comments directly next to complex formulae or other technical BS are most likely to be correct.
Some learn that then suggest getting rid of all comments or putting comments throughout all the code, but if we look at it another can can infer that some of it is code structure problems. A 2k line class with 200+ line methods with comments at the beginning of each method is more likely to have stale comments that any number of tiny classes with 10 line methods. Then comments on a 10 line method are more likely to be read too, because they need to describe less. Ideally they just add the "why" to the "what" the method's name provides. If the whole method and all the comments fit on the same screen then the dev ignoring it must be truly lazy.
The code gets updated but the comments stay the same.
It's especially sad when such practices survive code review. Reviewers, that's a symptom you aren't really reviewing what's going on.To me, those are the functions of commit messages, not code comments.
Of course, a "proper" IDE (and I don't know of any) would give you the context of the commit messages "impinging upon" any given line/block. Maybe something like Light Table (whatever happened to that?) could be configured to do this.
> Presenting an example usage of the function and example output
And that's what (formalized) docs are for. You can certainly generate those from docstring-style comments, if you like, but personally I feel like they get it the way of editing the code itself, and would be better kept in separate files.
(Consider: would you put the gettext translation table for a string, inline inside the source file that contains said string? The arguments are the same, I think.)
For the emacs users: vc-region-history is the best part of emacs 25. I totally consider it nice enough to motivate people to compile 25 by yourself if your distro doesn't provide it yet.
Really, rather than what I said above, it might be better if ever line of code were just always put in context of the PR+code review that allowed it to enter the codebase. Instead of git-blame(1), github-blame(1).
Yeah, it's definitely much more helpful to have it buried below hundreds of merge commit messages.
If updating the code block results in code with a different intent... you're not updating it, you're replacing it, and should take out the original comment with the original code.
If I have a code block to maintain data synchronization with a a server - that's the intent; if the code is updated from long-polling to websockets, the intent hasn't changed.
I know what you said, and I disagreed; responding back by repeating your disagreement doesn't add any value to the conversation.
Clearly written code already expresses what it does unambiguously. If your code doesn't do what it is supposed to do that is a bigger problem.
To distill that down, document a lot but don't rely on comments you find to be correct.
If the comments aren't really that important it will start hurting my ability to read and understand the code quickly.
likewise, if you a comment I wrote, you better sit up and read because it's there for a very good reason.
I'm of the opinion that at the end of the day people need to be able to read code, not use comments as a crutch. There are times when non-obvious things should be documented so developer B doesn't go down the same rabbithole you did, but in general the code should speak for itself.
document gives the context, the code gives the details. If you have both, there should be no real issues with understanding what's going on.
For a contrived example, why does a specific function/class use an array instead of a dictionary/hash table? A comment explaining that the dictionary was 500x slower in tests could fix that.
I suspect that is exactly the kind of comments you would want someone to "sit up and read" but I didn't want to assume.
That's ok - because they are there for reference purposes.
So you can ignore them most of the time.
When there's an issue, or you are reading unfamiliar code - you can reference them.
I leave notes in the code as to the oddities/undocumented nature of weird API dependencies, and of things that were tried in the past and failed.
So many times I've tried to compile some off-the-shelf thing and failed because I didn't read the release notes.
You just have to read the release notes :)
So when you're perusing an unfamiliar module, read the comments :)
I grasp that they can become stale, but on the whole, decent comments are better than none.
If a line of code has a comment attached to it, the reader needs to understand both the code and the comment. The comment is written in natural language. There are multiple ways of understanding it. It must be written with great care so that people reading it don't misunderstand it. Otherwise it will likely just confuse people. How about putting that effort in writing the code line with clarity? The reader must read and understand it in any case.
Stick with the code, people!
Also, code doesn't have to tell you _everything_. It doesn't have to tell you "what is not there".
Maybe we can both learn here if you elaborated what you think code cannot tell, yet it should?
The idea would be that the function names are so good that one does not need to read the function contents, right?
But some things are complicated and may not be expressible with the function name alone.
That's why people write comments. To help readers understand the code.
Let's say I am implementing some algorithm. I may mention the performance characteristics in the comment. Or I may let the readers dig into the code to find out what they are. Seriously, please don't follow "no comments" rule dogmatically.
If I have a function and I need to understand _how_it works_, that is, how does it implement some algorithm, of course I have to read every single line of code to understand how it works!
If the function is named "quicksort", for example, and I suspect it has a bug, I really have to read it to understand whether it implements the quicksort algorithm correctly.
It seems clear to me, as an impartial third party, that there are merits and drawbacks to both of your school's of thought on comments. "No" attempts to shutdown the discussion and makes it hard to discuss these.
Because in the cases wherein the code is abstract or dense, a little 'English' can go a long way.
Especially when making use of APIs that are weird and have weird behaviours.
AKA: "I'm breaking xyz convention here because API abc has the following odd, undocumented behaviour (i.e. returns nulls when it should not)"
Comments are not always good, but if there is any reasonable thoughtfulness to them, they will be 'mostly good'.
But just yesterday I came across a particularly good comment in an else block, that summarized the story of "how could we have gotten here", and it involved far-flung failures and not so obvious interactions. Very informative to have that right there.
While I do agree with this since thoroughly reading the code itself tells you the how I find comments that tell me what it does to be just as helpful as telling why it does it.
I used to fall into the self-documenting code camp back when I programmed in Objective-C and long descriptive names with named parameters made it read like English but as I've read more code in more languages I actually prefer shorter names supported with code comments that tell me clearly in English what the code does.
For instance earlier today I was reading some of the source code in fossil-scm in the check-in.c file. In it there was a function that was simply named `locate_unmanaged_files`. If there wasn't a detailed comment preceding that function I would have assumed it merely found the files and reported on them directly but after reading the comment explaining what it did I realized it stores the files in a temporary SQLite table, after reading that I learned that "locate" had a wider meaning then to just find it and return it but it rather meant that it is now located for any part of the system to find in the database. It would have taken careful reading of the code to have realized this and it helped me to better focus and understand the code I was reading.
Aside from "why", or explaining overly clever one-liners, the kind of comments I always find necessary are those providing before/after example of data during a transformation; and example strings w/ resulting capture groups for regular expressions.
Example: # "tag1,tag2,tag3:val" => tags: [tag1, tag2], metadata: [tag3:val] # fun one-liner goes here
Yes, you want test cases, and that's where you also put all the weird edge case conditionals; but, it's not like you're going to copy the actual line(s) of code into the test case, so you'd either need to temporarily copy the code (or the data samples) to be adjacent, or open side-by-side editor panes, or what have... All of this is more complicated than an example.
But what about sample data that doesn't fit on one line? Then either your sample data is too complex, or this method is unsuited to your situation.
There are guidelines for maximally legible code. There are no rules. If you follow rules, someday, you will encounter a situation where the rules force you to produce sub-optimal work. "Simply" strive to write legible code, and you will succeed. Strive to follow rules, and you will succeed at following the rules; and maybe you'll succeed at writing legible code.
Or the traditional C style with extremely long functions that do many things, cryptic names, hardware-related constants or magic numbers, little to no facility for modularization or composability etc.
In that era it was an absolute truism that comments were important. In today's era, comments are usually a symptom.
Best of both worlds or something.
I'm witnessing this currently with the code base I'm maintaining.
Yet this may just indicate that the code is complex and therefore required comments and contains bugs.
In any case, high comment density is a code smell.
What you think about spaces?
I agree. I was talking about the _amount_ of comments.
Extreme example:
void foobar()
{
// iterate over the list
foreach(var item in list)
{
// send it
sender.send(item);
}
}
I've seen a lot of comments like this. They don't add value. They distract. They must be removed.They also tell that the author didn't have a clear understanding why code comments should be written, so we may not trust his/her judgement in other programming decisions. The code is suspect. It's a code smell.
Not a problem for me. Usually, I clear them out.
/says so many people I've worked with.
I disagree. The _what_ is what your comments should tell me. The contract, and its preconditions and postconditions. Nothing more, nothing less.
If we are speaking of API documentation (i.e. function or module documentation): The "why" can't be explained here. It's at the caller side.
There are more rare cases where it's reasonable to document the implementation of a function because there's a particularly tricky section of code -- and documenting the "why" makes more sense to me here.
Otherwise we need to know via the comment (contract) what is the what, so that we can fix the bug (or even know that there is a bug).