A few weeks ago I came across an interview with an academic software researcher, now working for Microsoft. (Unfortunately the interview was in a non-English print publication, so I can’t link to it.) The interview was quite interesting, far better than I’d expect from a magazine targeted at a wider audience; it certainly wasn’t a trade publication.
Near the end of the interview, I had to stop reading and went “What?? That’s the stupidest thing I ever heard!” This was where the interviewee said that one distinguishing mark of good code is the lack of (inline) comments. Properly written code, the theory goes, is so clear and simple that it needs no comments.
This was a bit of a shocker for me. I have some software development experience, and in the fields I’ve worked in, lack of comments in code is a clear mark of poorly written, sloppy code. Even suggesting that code should not be commented would be an obvious sign of utter ignorance, perhaps too strange to even take seriously.
That got me wondering… Are there perhaps very different worlds in software development? Maybe some software development is done in enough of a vacuum (so to speak) that the developer has complete freedom to design the software and all its interfaces. In such cases, it may be possible to write highly self-contained code which is simple and clean enough that it doesn’t need comments.
But in the fields I have experience with (device drivers, operating systems, virtualization), that just isn’t how it works. Software development isn’t done in simple layers or modules but rather resembles complicated jigsaw puzzle pieces which have to fit in with other software (and hardware) on many sides, and other people’s interfaces are not necessarily straightforward.
When interfacing with hardware, it is especially necessary to judiciously comment code. Saying that source module foo.c was written based on the ACME DigiMatic 3000 data book is one thing, but if said data book has 1,500 pages, that’s not very helpful.
What’s worse, often there are the things the data book doesn’t say and which had to be learned from experience, be it quirks or outright bugs in hardware. Fixes and workarounds for such issues can look rather strange and without comments, they would have to be considered bugs in the source code. Here’s an example:
if (regContents & SERIAL_IIR_FIFOS_ENABLED) { // // Save off that the device supports fifos. // Extension->FifoPresent = TRUE; // // There is a fine new "super" IO chip out there that // will get stuck with a line status interrupt if you // attempt to clear the fifo and enable it at the same // time if data is present. The best workaround seems // to be that you should turn off the fifo, read // a single byte, and then re-enable the fifo. // WRITE_FIFO_CONTROL(Extension->Controller,(UCHAR)0); READ_RECEIVE_BUFFER(Extension->Controller); // // There are fifos on this card. Set the value of the // receive fifo to interrupt when 4 characters are // present. // WRITE_FIFO_CONTROL(Extension->Controller, (UCHAR)(SERIAL_FCR_ENABLE | Extension->RxFifoTrigger | SERIAL_FCR_RCVR_RESET | SERIAL_FCR_TXMT_RESET)); }
The above snippet is from Microsoft’s own Windows NT, namely the serial port driver in the Windows 2000 DDK (file src\kernel\serial\initunlo.c). It would be difficult to argue that Windows NT was written in an unprofessional way. Yet it’s full of comments… and it’s fairly obvious why.
Without the comments, the workaround for broken hardware would look like a bug—why disable the FIFO and read from the receive buffer when we’re trying to enable the FIFO? Without the comments, the next programmer might well try to streamline poorly written code by removing redundant bits… only to break it. Since this is a workaround for a hardware bug, it may not be documented in the hardware manufacturer’s literature, either. Or it even might be, but since this is a standard chip produced by at least a dozen companies, one would have to read the documentation for all of those.
In other words, a comment can make all the difference between a bug and a feature. Not adding such a comment would be highly irresponsible. If academics don’t like that… they can always write device drivers themselves—without comments.
Even in the case of self-explanatory or trivial code, comments should be added to summarize what a group of sentences do. Or what the whole code does. Because one doesn’t always need to understand every atomic step in the process, but just a few (and big) steps, or just the main purpose. “Can’t see the forest for its trees.”
The (academic?) ideal is writing code in the highest-level language available for the job. That way only the core program logic needs to be explicitly expressed in the code, and no comments are necessary. If you need in-line comments, you’re doing something wrong, the theory goes. Note that comments describing the purpose of functions and modules are considered acceptable.
Of course these rules being academic, they completely ignore reality, i.e. 90% of the time as a professional programmer you’re probably going to be working with code that someone else wrote. And you can only hope the original author ignored the “rules” and commented the code appropriately 🙂 In fact even your own code written 5 years ago might as well have been written by a different person.
Heck, sometimes I have trouble understanding even code I wrote five days ago – a clear sign I didn’t comment it properly! The trouble is never understanding what the code does, that’s clear enough, but rather why it does what it does. The whys are always dependent on some external behaviors or interfaces, and that’s where comments are necessary.
The “no-comment” mantra is more relevant to high level languages (especially JVM ones) but it’s still important to document everything which is not obvious.
I think it’s perfectly legal to comment drivers, OS specific code and/or any case where just reading the code is not enough.
Also if you comment it should not mean that you can write code like:
int x=3; // this is the starting line
Z:C(x); // load file from starting line 3.
@michaln: keep up your good work and please post more often.. I need more OS/2 stuff.. I’m starving..:)
Not to mention the joy of compiler/assembler bugs where certain logic flows have to be done in some way, otherwise things explode… Not that I’ve seen the NT source, but rumor is the Dec Alpha compiler had some major issues that required some odd work arounds. Even Linux winds up with stuff being done in weird was because of how GCC may or may not optimize things…
But yeah if anything the more comments the better.
I can’t understand how someone would think that lack of documentation/comments/specs would be a good thing.
I’d post more often if I could devote 50%+ of my time to this site 🙂 But I do have a few things queued up.
I know that feeling all too well… !
I’ve heard this several times from academics.
They say that you should not comment a single line, because that means the line was written in an unclear manner. They think it’s bad when you write a large block comment, because the comment will just get out of sync with the code.
This sounds reasonable in principle — but simply doesn’t work out in practice. Any possible harm from putting in too many comments is outweighed ten-fold by the *certain* harm from not putting in enough comments.
Why comment a single line? Because sometimes it’s *not* obvious what it’s doing. Because sometimes you’re narrating a single line that precedes a larger block — so even if that line is obvious, it may not be in the context of the entire function. Because that one line may turn into 10 lines after bugfixes, and the seemingly-superfluous comment today may turn into high-level overview in a year.
As for using the risk of falling out of sync as a reason not to comment — that’s like using the risk of obesity as a reason to starve yourself to death. It simply makes no sense. If you read a comment that’s out of sync, then you can at least put it in a debugger and test whether the code is behaving as the comment says it does. Without a comment, you have even less of a clue.
Donald Knuth was a proponent of “literate programming” — of mixing code with human-language narrative. He was also an academic — but he also wrote TeX. In other words, he wrote enough code that he gained practical experience on the value of narratives.
A previous commenter identified what the academic world seems to miss (the elephant in the room, really – how could they miss it?): Even if the language and coding style used make it clear as day what the code does, you still need comments to explain why it does what it does. The code itself cannot show the background story about the problem or processing you’re solving with your code. Even a simple thing like referring to a requirement ID number.. how are you supposed to do that without adding comments to code?
(At the place where I was a student in a previous lifetime the policy was that teachers went out in the real world to work now and then.. before going back to another round of teaching. Sound practice for avoiding the pitfalls of only living inside academia.)
-Tor
I personally even comment out code to be replaced during optimization cycle and /leave it there/!
That’s sensible… especially if the optimized code is much harder to read and understand. And if the logic needs to be changed in the future, it’s probably easier to go back to the original unoptimized version anyway.
Wow. Talk about misunderstanding. You’ve put inline in parenthesis because to you it looked like a minor detail while in fact it’s the exact opposite.
FUNCTIONS are supposed to have comments (and good, sensible, names, too). These are blocks which are doing something tricky. For reasons of efficiency sometimes MACROS are used instead of functions. These are all fine and good.
But if you see the INLINE comment then it usually means that you are trying to cram too much into a given function. The code above is perfect example: it contains one useless comment (“save off that the device supports fifos”: really? who can guess that FifoPresent = TRUE means “fifo is present”?) and two comments for tricky pieces of code which should be put into their own functions or macroses. Names similar to UNCLEN_BUFFER_WORKAROUND and ENABLE_FIFO_WITH_DEFAULT_SETTINGS will make it clear what goes on here without any comments and fine details can be read where said macroses are defined.
Practically speaking it may not be good approach for one reason or another, but it’s easy to see why this approach makes sense (and where it fails: if there are too many interrelated workarounds then you can not split them into nicely separated functions). More often then not such inline comments are indeed a strong hint that you’ve overreached and it’s time to refactor your code.
I suspect you’re failing to see the difference between “tricky code” and “code whose purpose is not obvious”. There are simply too many situations where there’s a need for code which is perfectly straightforward per se but seemingly redundant or confusing. That’s very different from code that is hard to read and/or understand. There are things like, say, a quicksort algorithm which aren’t terribly clear unless one is familiar with them, but which can be understood by careful reading of the code. Then there are things which make no sense even after going over the code a million times — unless there are explanatory comments.
The suggestion that one-time actions should be broken out into separate functions/macros just so they could be properly commented is frankly ridiculous. Needlessly breaking a linear sequence of steps into tiny functions or macros would only serve to obscure, rather than elucidate, the purpose of code.
Remember the debian disaster where the RNG was limited to only 32K distinct random numbers? This was the result of someone who did not understand the code and removed a seemingly unneccessary “dead” assignment which was deliberately placed there to force a lot more entropy.
I think this is much about the mistaken belief that there is nothing in between code with *no* comments, and code with *too much* comments.
I work as a programmer, and personally greatly prefer it when code can be written in a way that makes it clear what’s going on without needing comments. However, it’s pretty rare that a whole, non-trivial program can be written that way. In a large, complex system that interacts with other components, some of which might not even be under your control, it borders on impossible. Refactoring is a useful technique but can only go so far because at some point, the entire program becomes a mess of one-line functions; refactoring should always strive to *improve* clarity and/or maintainability. It’s a fine line to tread that requires careful judgement whether you are actually serving that goal.
I’ve had a few instances myself when fixing bugs in production software that (what turned out to be) the fix was completely non-obvious at first. There was nothing to be gained by comments describing *what* the code I ended up writing to fix the problem was doing, because the *what* was blindingly obvious, but there was a lot to be gained by explaining right then and there *why* there’s this block of seemingly-nonsensical code. That way, if someone comes along later and pokes at that code, there is at least a big sign next to the entrance to the dungeon saying HERE BE DRAGONS and explaining what to watch out for, complete with a reference to the issue tracker ticket.