The most common reason I write comments is to explain the purpose of something that is unintuitive from the pure code. Examples are comments in CSS about a particular browser quirk, or hacking around an edge case in an efficient but opaque way.
One of the first things I'll do encountering a feral code base is run an automatic documentation generator over it, even if there are no API comments, because many will produce at least some level of documentation from pure code, including cross references, a type index, call graphs, type diagrams, etc. This can be especially helpful when the code is poorly organized, and trying to trace simple program flow in an editor means navigating a dozen modules manually. Doxygen, for instance, will produce hyperlinked program listing, so that I can use a browser in a natural fashion to navigate the code structure and program flow. The browser can maintain virtually unlimited context, whereas my brain loses track of where I am once I'm seven levels deep in function call nesting.
Some IDEs and UML tools also are capable of reverse engineering documentation from the code base. The Togethersoft tools used to be excellent at grinding through code (and may still be, but I haven't used them in years).
RE'ed documentation of feral code can reveal how well (or more frequently poorly) the code base is structured, and identify key areas for architectural or design refactoring (if that luxury is possible).
In writing my own code, I decompose until each function or method has a single purpose (f() does X, not X & Y & Z!), and therefore the API documentation suffices to document the code itself. Rarely do I write a comment inside the body of a function or method. That happens when I re-visit the code, and discover that it's operation is non-obvious. The non-obvious stuff tends to be the tricky stuff it took some time to get right, and so it doesn't get mucked around with, and such internal comments rarely go stale.
I wait until re-visiting the code because authorial bias (my code effectively become's someone else's after several weeks, sometimes faster :-) ) obscures what is and is not obvious. I used to over-comment from a tendency to perform a mini-brain dump in comments -- but the knowledge required to WRITE the code (this is what I was thinking at the moment) is no reliable indicator of that required to READ the code (this is what _you_ need to know).
(I've theorized that having someone else comment the code from the start, just like having an unbiased tester, could make for better comments -- wherein the commenter is also necessarily a code reviewer as well. I've never gotten any of the places I've worked to agree to 'cross-commenting' as a standard practise, but most love worthless, perfunctory desk-checks prior to check in.)
To avoid comment churn, and because I refactor aggressively when creating brand new code, writing API comments is the LAST step in coding.
Finally, I developed a habit of writing comments exclusively in point form, because context switching from programming constructs to proper English grammar broke my flow. The point form comments feel like a miniature brain dump, whereas otherwise I'd pause to think about how to put the information into a proper sentence, and then make nice paragraphs, and suddenly I'd be channeling me from 7th grade compsition class. It's also easier to scan and digest comments as point form notes.
That's what I do, and I leave it at that, because telling someone else how to code is like telling them how to raise their children.
tl;dr version
- at least write API comments, pls
- doc generators (and other tools) sometimes are a great way to RE docs for feral code
- write comments in point form
- write API comments as the FINAL step in coding
- try to remove authorial bias from comment writing
/// - parse an HTTP input stream as XML
/// - requires a progressive parser (currently xpat, which is distributed with
/// Apache)
/// - the XML in turn is mapped to database operations, and the results
/// of the database operations used to create the HTTP responseI hear this one a lot, from a lot of different sources. In practice, it never seems that practical. Here's a trivial example: the dashboard-page function in our webapp. It needs to do all of the following:
1) verify that the user is logged in. If not, bounce them to the login page, then bring them back on successful login
2) collect the list of all their providers
3) collect the list of all their profiles
4) collect the list of all their account information
5) load up and display the appropriate templates to render the web page with the requisite information.
Now, points 1-4 are accomplished by calling other functions, true. But dashboard-page still needs to do 5 separate things.
// - control the login sequence
function login_sequence() {
var user = verify_login
if (!user) {
user = login();
}
var providers = collect_providers(user);
var profiles = collect_profiles(user);
var account_info = collect_account_info(user);
load_templates(user);
display_templates(providers, profiles, account_info);
}
(Forgive the guess at what your code might look like.)State may be passed between called functions, and used in control decisions, but state should not be grossly manipulated in sequencing functions (I do find with this style of programming that at high levels the state passed around tends to be large 'context' objects, rather than granular arguments encountered at lower levels). What I would NOT want to see in such a hypothetical login function is ALL the actual lower level code to do the login, collect the data, etc., so that essential higher order detail is obscured by the lower level operations.
A function's API comments do not need to repeat the purpose of called functions.
As I come up with API comments last, I usually think about them in reverse -- it's not 'I need to think of the single purpose of this function before writing it', but 'what single purpose did this function end up serving?'. Not being able to think of a decent answer for the latter is a possible symptom of sub-optimal decomposition. Then again, cutting blocks of code and pasting them into their own functions has become an instinct rather than conscious decision for me, so I'm effectively anticipating writing the 'single purpose' API comments.
At a certain level of detail I don't need to know the minutae of login, just that there is some black box function that controls the lower level details. And if I need to know the details, I break open the function and follow its call flow (or look at the autogenerated call graph in the doxygen docs or similar).
Having read a lot of feral code, I find the major indicator of quality is the static navigability of the code base (i.e. can I find my way around just by reading the code in an editor, without resorting to debuggers or autogenerated documentation), and having a level of detail structure, akin to the zoom feature on Google maps, is one method of achieving navigability (and partially the value of OO techniques). So it's okay to have functions/methods that simply sequence or aggregate calls to lower levels, and to describe them as such.
It was mentioned elsewhere on the thread that debuggers are useful tools in understanding a code base -- and I do often find myself setting breakpoints on code because it's near impossible to understand how particular functions get invoked by just reading the code. Then examining the call stack at the breakpoint I see that event loop called the network code invoked some code to read a database, which called into some code to instantiate widgets, which called back into the database code, which called the code that calculates order totals and tax, which called the widget code again to update those fields, all of which goes 30+ levels deep.
If you confine yourself to reading code, but no other comments or documentation, you may think the code's behavior is correct, even though it's buggy.
I'm also at least slightly troubled by the assertion (not made Aschwin, but others) that the code is the only artifact worth reviewing. If you have a specification, or documentation, that doesn't line up with the product, why do you have those documents in the first place? Again, I don't expect that those documents will outline the specific implementation (though, if your shop uses software design documents, they should), but I should be able to get a reasonable explanation of how the system works from the docs. If this isn't the case, how do you expect to have someone test the system?
All that said, I agree that having the ability to read and understand others' code is critical to being a good programmer.
// Replace with a link to the core
AddStartupShortcut("Shell.lnk", Path.Combine(INSTALL_PATH, "foo.exe"));
// Update device time:
_log.Information("Updating device time...");
SetDeviceTime();
I always intend on cleaning these up, but often never do./* Does everything required to initialise the UI (DO NOT CALL DIRECTLY - see class foo) */
Get outdated quickly (without being updated) you have big problems.
Equally a one line comment before things like:
// Do this if we can be certain we are in month X
if (!(!x || ($chk(diff(y,o)) && (z<p))) {...}
Saves you having to draw out a truth table each time. Even if the logic gets tweaked a bit when bugs are found the purpose of the code is likely to remain and so the comment won't age too badly.
Of course, as long as we are using ASCII text for editing code, this is impossible. A few people are working on fixing this flaw (Intentional Software, Jetbrains MPS) but don't hold your breath!
Sure, someone could read all the code, but it's way faster to just skim and pick out the comments if you don't care about the details. Think of code commenting as a time-saving device.
Basically, the comments were text and relational algebra in LaTeX which explained the implementation of dataflow equations by using sets and those relational algebra expressions were then implemented in operations on binary decision diagrams. However, as I said: this does not occur often. In fact, one might call it rare ;)