Personally I don't write a whole lot of comments, but common types of comments I do write are:
- (sketch) proofs that my code is correct
- explanations why not to just do the obvious thing (e.g: optimization was required, the API I'm calling is shockingly misleading, the "obvious thing" led to a subtle bug)
- a description of the input edge-case which necessitates special handling, thus explaining why the special handling code appears
- a reference to the requirement or specification which mandates the behaviour implemented by a particular line of code
- top-level description of a series of steps taken: ("first get the input", "now strip the wrapper", "finally parse it"), which aren't needed if the names of the functions called for each step are well-chosen and unambiguous in context, but I'm not going to write a do-nothing wrapper for a generic function just to give it a more specific name for one single use
- documentation to be extracted automatically. This is a large proportion of comments by volume, but I'm not sure it "really counts".
- things that could be part of the documented interface, but aren't because they might need to change in future, or because they're internal helper stuff that outsiders don't need. Hence, useful for the implementer to be aware of, but not for the user. Private class invariants are included in this - do you really want to read through the whole class every time you want to rely on "this member is non-null", or "size is no greater than capacity", or "access to this member must be synchronized"? Better to check the description of the member before assigning to it, to see whether you're allowed to. If someone doesn't have the discipline to avoid breaking a clearly commented invariant, well, no wonder all their comments are wrong...
Some of those things are handled by comprehensive tests, in the sense that if the tests pass then the code is correct (hah), if you have the test code open too then you can see the special input cases, and if some fool hides the comments, makes the same mistake I originally did, and refactors my code to do the obvious thing, then the tests will fail. That doesn't mean comments don't handle it better, in the context of reading through code. In none of these cases is reading the comment a substitute for reading the code, they're designed to highlight what's important but not obvious.
Of course in the case of auto-docs, it's perfectly reasonable, and probably more usable, to view the documentation separately.
When debugging, as opposed to making changes, it's way more valuable to let the code explain itself. Comments are less useful, especially those private invariants, because if you're debugging then there's a reasonable chance that whoever wrote the code+comments made a mistake, so the comments may well be wrong. On the plus side, if you see a comment that doesn't reflect what the code does, there's a reasonable chance you've found a bug. If you just see what the code does, it might be some time before you realise that what the code does isn't what it's supposed to do.
Sometimes good naming goes a long way towards replacing comments. However, names can be just as misleading as comments if they're wrong. And, let's be honest, frequently names are at least slightly misleading or ambiguous. I doubt that the author of that blog post would go to the extent of replacing all names with "var1, var2, func1, func2, class1, class2 ...", just to make sure that they weren't distracted by the original author's incorrect intentions for the code. And I don't think it's any truer to say that "comments are always wrong" than to say that "variable names are always wrong". They're written by the same person at the same time with the same purpose.