A lot of people had comments about my comments on comments in the last issue. I thought there would be some responses advocating for no, or limited, commenting but strangely there were none.
Here's some of them. I shamelessly stole the subject line for the title of this section from Rod Main's email:
In "The Embedded muse 319" you wrote "I've talked with several engineers recently who told me they don't comment. Ever. Their code is completely self-documenting so comments aren't needed. One argument is that the code inevitably drifts away from the comments, and wrong comments are worse than none at all." This, as you said, is completely unprofessional. The argument that "the code inevitably drifts away from the comments" is not, IMHO, a valid one. The comments should have been changed to reflect what the code is being changed to do at the same time. However, if someone has a mind-set that they don't comment then clearly changing comments is also going to be beyond their ability too. They are the reason that the code drifts from the comments.
a= out_pr * scale; // scale the input using the calibrated coefficient
if(Mark2) // mark2 board components mean there is a
// -2.6mVolt offset which must be corrected for.
Take out the comments and looks as if its self-documented. But in 6 months' time when you are updating for the Mark3 board, you won't remember why the offset was being added or if its necessary for what you are doing now. Been there, done that.
Unless you are writing code with psychic abilities which adds commented explanation as you code then I think self-documenting code is an illusion. There's only code you remember knowing what it was supposed to do when you wrote it and are surprised that you could have written something that you now can't see what it does or know why it was done that way. If only you'd put some comments in to document it properly at the time…
J.G. Harston contributed his thoughts. The link is to a compendium of CRC-16 implementations, including in PDP-11 assembly language!
> Do you use in-line comments? ... or a block comment instead
I use both. I tend to use three sets of comments:
* a block summarising code entry/exit
* end-of-line comments through the code where needed
* a single line introducing a distinct subsection of a block of code
Plus, I use an assembler that allows multiple instructions per line, so I group whole atomic actions together, for example:
LDA crc+0:EOR #&21:STA crc+0 :\ CRC=CRC EOR &1021, XMODEM polynomic
TYA:EOR #&10 :\ Get CRC high byte back from Y
This makes for ease of readability as the comment describes the action of the code instead of the action of the individual instructions, and supports the goal of having single distinct code blocks being no bigger than a display screen so you can see the whole code block in one go without scrolling stuff off the screen. I'm showing my age when I say that's a 80x32 screen.
I am paranoid about not understanding my code when I come back to it, plus I try and write code with the intention of other people reading it and as a learning resource. A habit picked up at university and writing for magazines. I'm regularly surprised that youngsters don't have this habit - how did they expect their assignments to be assessed?
While not claiming it is the best, I try to stick to this style of commenting: http://mdfs.net/Info/Comp/Comms/CRC16.htm
Jon Daley wrote about a style I have seen too much of:
When I was teaching an undergraduate engineering course, my students would often complain about my requirement for commenting, and would say things like "it's self documenting", but when I would force them to write comments anyway, they would write things like:
// function that returns an integer
int a; // we need an integer to store stuff
a = 5; // set a to 5
a += 4; // increment a by 4
return a; // send a back to caller
And they would be quite irritated when I didn't think those comments were useful, though it does explain why they thought their code was self-documenting...
I think comments like that are actually worse than not commenting, as they don't give any new information (such as why one would want to increment the variable by a seemingly random number) and quickly get out of date if the code is ever modified.
I think storytelling is a great way to get ideas across, and Dave Telling had one to share:
Re comments - I agree with you 100%. I have found that I often cannot remember why I wrote a function a particular way when reviewing code months or years later, and that is why I'm a big advocate for comments, even when some might say that the code is obvious.
I had a situation a few years ago, where we had a division that had done a fairly complex EFI application in assembler (68HC11) and there was a desire to refactor in C. I took a look at the original source, and most of the code had no comments, and those lines that did often had comments that NEEDED comments to be able to know what was going on. The worst was that there were a number of code blocks that used complex stack manipulation, and were (to me, at least, as an engineer, not a programmer) almost incomprehensible. We asked a well-known firmware company to give us an estimate of what it would take for them to do the conversion, but the cost was so high that the idea was scrapped. I later found out that that same division had already done a new EFI project (but using a different uC) that DID use C for the code, but the division was shut down soon after.
I have to really force myself to slow down and add comments when I write, and I'm also trying to make sure that I incorporate the idea of writing a comment block that explains what the function actually does; what it expects to receive when called, and what it returns (if anything). This is good counsel for anyone!
Paul Carpenter wrote:
- positive range limited
- zero disabled
- negative a series of error codes
One thing I like about Ada is that one can explicitly specify ranges for variables, which are checked at runtime.
Mark Globerson shared some experiences:
I liked your suggestion about focusing on block comments. I have worked on many embedded systems,coded in both C and assembler, that had combination of algorithmic or protocol like code spread out in several places in the design and often had a pipelined approach to solving problems so work was done in stages and setup for the next stage. Events occurred that required updates to certain variables that would signal other areas to be updated. This work was mainly done in memory and speed constrained DSP chips that did combinations of algorithms and bit banging. I found that I needed a large area of block documentation to describe the rather complex interactions of the various pieces in time and the particulars of how variables/data structures were used to coordinate the work. I also tended to keep hand drawn timing diagrams of pipelined processing to track how the stages worked. In addition to having an overall description of how everything worked in one area I put in pretty detailed comments in the various places that subtle things were being done in the code as a way to remind me of how it tied back to the overall algorithm. The problems the code solved were complicated and the design + implementation took a long time to figure out. No this wasn't because it was spaghetti code and no we couldn't treat this like high level language code and modularize everything down to trivial routines nested in a tight object hierarchy. Limited memory and extremely tight real time deadlines causes you to make some compromises in a way that someone writing a web application would not be familiar with.
I understand how people could feel that comments could get out of date. My feeling is that when you have something this complicated it is your job to update the code and comments together, taking the time to make sure they are consistent. When I was asked to go back and modify this code at some later point the comments (and my hand drawn timing diagrams) were invaluable to help figure out exactly how things worked so changes could safely be made with a thorough understanding of the details on how the system worked. The comments saved me hours of work and gave me the confidence to believe that the changes were correct. I'm sure there are some people out there who feel they are code gods who are above this documentation. I consider myself pretty mortal and fallible. I still make mistakes and it's nice to have some way recreate the thinking processes that went into the design in order to find those mistakes or make changes.
On the flip side of the coin I have had the task of reverse engineering some fairly complicated systems where the code was written in assembly language. The documentation was sparse, at best, and after weeks of pouring through the code I found many conditions that didn't work, though it was clear the code might or might not end up hitting those conditions, depending on inputs and timing. Since I had inherited this code I made sure I documented the overall way it worked and the subtle interactions in, knowing that things would change. Several releases of this code were already in the field and required support so somebody had to know how to fix. Even if the code was well written and well documented it would have taken time to figure it out. Maybe this could be considered a rite of passage but I found this to be a completely undisciplined way to design a system. It was clear that the initial design process didn't include the use of pencil and paper to write out the overall algorithms used and the system timing. So the code was started without a clear picture of how it should work. Then it looked like a series of patches to handle odd cases that occurred due to a lack of generalizing certain parts of the problem. At a minimum having any documentation that attempted to explain the overall flow would have been invaluable. After putting in my time documenting things I kept this documentation up to date as future changes were added so there was some way to understand the code flow without spending weeks reviewing it each time you needed to make changes to the complicated parts. That is when I learned that I document because I'm lazy and don't want to repeat work over and over.
So yes I prefer block comments that explain the high level view of what is really happening and how things piece together. It's useful to have comments on the more local level when subtle things are being done within a function/procedure/code block too, but please don't bother with:
foo = a + b ; /* add a to b and store in foo */
David Wyland wrote:
General Systems Theory states that structure and function are not related, except by the designer/user.
Neither can be derived from the other. A structure can have many possible functions. A function can be implemented by many possible structures.
If comments exist, they are usually about structure: a description of what a line or block of code does in terms of how it does it. This lets you verify the line(s) of code: does it do what it says it will do? Is the design of the structure accurate?
What is usually missing is what is the routine is expected to do for the program that called it - its function?
Does it do what the calling program expected? This validates the function.
For example, are you taking the square root of a sum of squares, or the square root of your telephone number?
It does not matter how well you do the wrong thing.
Make sure you describe both what it is supposed to do as well as how it does what it does.
Dave Harper contributed:
In the latest EM I found your "Comments on Comments" section very interesting. I generate a lot of code for my own use which means I am also the one who needs to maintain it. Thus I have a vested interest in learning anything that will help me comment more effectively. I agree that well written code is somewhat self-documenting, but it only shows "what" is being done. Over time I've found that a far bigger issue in maintaining code is "why" something is done the way it is. Often there may be subtle corner cases that result in code being written one way over a more obvious way and the code itself doesn't convey this to the maintainer. Thus I've developed the habit of a comment block preceding the function that describes why it is needed, the purpose of any non-obvious variables, etc. Further, if sections of the function are not obvious as to why they are doing what they're doing, another small multi-line comment block is likely in order.
Brian Rosenthal improved my example:
In general, I find that comments that explain "Why?" are better than comments that explain "What?". Using your (admittedly simple) example:
a= out_pr * scale; // scale the input
is useless - I can see by the code that we are scaling something. The improved comment:
Adjust the input by "scale", which
is the coefficient calculated at
is better, but I would suggest that the same information can be conveyed by changing the variable from "scale" to something like "calibratedScaleCoef". To me a more useful comment would explain "why are we doing this here or now?"
Scale from bits to real-world units now;
as all following corrections will be in
Likewise, anytime you spend a day changing something, only to find out why it was bad idea, note it in the code.
Don't scale to real units yet, this all
needs to be streamed in binary first.
When writing code always assume that 10 years from now someone (probably you) will need to change something and a bit of context makes all the difference.
Our small group of two or three has been maintaining the same code base as it has moved to updated hardware for 30 years - my part has been "only" 17 years. As it isn't possible to keep track of hundreds of thousands of lines of code in your head, comments that explain "why" really help.
Harold Kraus had a different take:
I was taught the importance of commenting in my first programming courses (early 80s), kind of in the vein of "show your work". But, today, in a safety-critical work environment where I have to trace every line of code to requirements and test cases, I find that my code is consequently (1) very simple and (2) well explained in the high and low-level specification/design items. It is not rare that code comments I write to keep track of what I am doing as I try to solve problems end up as low-level design data. I am loathe to maintain design data in two places. What I think some coders might put in comments, I put in design and requirements, which kind of leads me to the thinking that seeing lots of comments reflects a need for more detail in requirements and design. Now, I know there are ways to set up comments and other tags in code that assist in automating synchronization of code and design (e.g., Doxygen), but I haven't put in enough effort to set that up in a way that works for us.
P.S. More to the point, while I am specifying and designing, I am visualizing how I will code and test; and while I am coding, I am visualizing how I will express specifications, design, and test.
P.S. Years back, I was taken by Scott Ambler's writings on Agile Modeling; this was after my first Level B project wherein I felt constrained to use a single notation model and after my second Level B project wherein I codified how I used multiple notation models.
I like the statement in "Agile Architecture: Strategies for Scaling Agile Development - 6. Requirements-Driven Architecture": "Your architecture must be based on requirements otherwise you are hacking, it's as simple as that."
George Farmer sent this:
I was shocked to read in EM 319 that there are still engineers out there who mistakenly think their code is self-documenting. I find this extreme hubris on their part almost criminal – certainly a firing offense in some cases, to say the least. They have no idea how much they actually cost their employers in the long run, not to mention the extreme risks they are taking.
Having dealt with someone else's so-called self-documenting code from my days in aerospace and later in the off-road, heavy construction equipment industries, I can share numerous anecdotal horror stories, but I'm sure you have heard them all before. For what it's worth, I have gone back through some of my old code from many years ago and was glad I documented and commented to the level I did – even then I regretted not having commented more than I could have.
I like to joke that I have a very good memory, just that it's very short. The plain, simple truth is that the latter part really is true and is no joke. In this faster-better-cheaper-pick-any-two world of Embedded Development, very, very few people have the gift of photographic memory (I know I'm certainly not one of the Gifted). Even thinking that code can be self-documenting can be extremely dangerous.
Paul Bennett wrote:
As one who is a stickler for having a decent commenting strategy I find
that things improve in a "Component Oriented Development Environment".
This environment encourages a more literal style of programming where
the comments are written first.
I use the block comment approach to capture the specific portion of a
requirement for the component under design. Writing the comment as
though it was a requirement specification has a number of benefits.
1. You can review proposals for components without having written or
coded the component.
2. Reviews of the comments, provided they are written as a component
requirement, can be gauged for validity against the higher level
3. When the code is written, it can be reviewed and tested against the
already validated comments for correctness of implementation.
4. If component limitations are made clear for components within the
comments, the comment acts as a data-sheet for the component
when it is made available in a library, thus allowing sensible selection
of components that are re-usable.
The above benefits are already enjoyed in the hardware world whether
mechanical, electrical or electronic, and I see no reason why software
should be any different.
Mat Bennion had a good point about maintaining comments:
I'm sure you'll have plenty of response to your comments on comments. My point is that there should rarely be a maintenance overhead in ensuring that the comments match the code because the comments shouldn't simply be a human-readable description of the code - we can assume that the reader is fluent in the language. They should convey information that is not presented directly in the code - e.g. its intent, anything surprising about the algorithm, rationale, tips to help the maintainer... this is likely to be much more stable than the code itself.
Claude Galinsky had this story:
Ten years ago I worked for a company engineering high end music synthesizers. Over the course of the company's life, it had been sold multiple times, the last one involving massive layoffs of the developers. Those of us hired afterward to re-establish the company needed to migrate the technology from 68000-family code to a more modern platform. We were confronted with a large real-time C code base, controlling our custom VLSI chip, that was almost completely uncommented. Not only that, it made such extensive use of (also uncommented) macros that it might as well have been written in Klingon.
The mastermind behind this code, who had quit the company angrily after they had laid off all his friends, was a brilliant but prickly ex-Bell Labs guy who, unfortunately, thought of himself as a machine. His take on the need for comments was simply this:
"Comments don't compile."
It was necessary to sweet-talk him for weeks before he agreed to come back as a consultant. Then it took him and the brightest of our software engineers most of a year before we were able to get it working.
If there hadn't still been one person at the company who was still friends with this prima donna, the company would likely have had to shut its doors.
Ron Aaron wrote:
Regarding code-comments: I've been writing software for over 30 years, and have worked in a very wide variety of places. Everywhere I've worked had a policy (whether enforced or not) that code must be commented. In any kind of team environment, uncommented code is a huge liability.
But even when working solely on my own projects, I have always commented liberally enough so that when I return to the code in six months I don't have to spend time figuring out what the code is supposed to do.
Which brings up the only valid point against commenting, which is that comments must be maintained just like code. When the code changes, the comments must be updated to match (and indeed, they must match to begin with!), or the comments can actually be harmful because they will mislead and waste the developer's time and his company's money.