Tags
computer code, computer programmers, computer programming, programming language, real programmers, software development
There is a common tendency, as we crank out code, to think that we’ll come back later and write some really good comments — comments that future reviewers will view with admiration for their clarity, completeness, and humor. But if we’re honest we have to admit: Later never comes. We never go back and write those great comments. Often we don’t write any comments at all other than some placeholder we might dash off at the time.
Which is why Rule #4: Comment As You Go!
There is a practice — and I think it’s a good one — of writing a detailed and descriptive function, method, or class, comment first, before writing any code. Essentially, you describe what you’re about to code.
For one thing, explicitly describing a design helps clarify your mind regarding the design. In some cases, a detailed description makes the coding trivial. In others, the description may make you aware of lurking problems you hadn’t noticed.
There is an ethic that creating software should involve 90% design and 10% coding. There are design methods that evolve designs through refinement stages and ultimately into the code. Literate Programming seeks to turn high-level descriptions of software into code. Fast Prototyping seeks to turn increasingly complex models into refined products.
[The first programming job I applied for involved converting flow charts created by software designers into COBOL code. This was a low-level entry position — the coding was viewed as trivial. The real work was done by the guys (and it was all guys, of course) who made the flowcharts.]
So writing a detailed comment describing the function, method, or class (or a lot of things; let’s just say “object” from here on), focuses and clarifies the mind, plus it’s the right thing to do for good designers. It also ensures that a good comment exists!
It’s a bit like how, in Test Driven Design, you’re supposed to create the test(s) first. It guarantees the tests do get created. Write the comments first for the same reason.
[For the record: I think TDD is a good thing sprinkled lightly throughout a project. I think it’s possible to get carried away — I’ve found myself spending more time creating tests than code. (OTOH, I love the robustness this creates.) The thing is, you can never create a complete test suite — complete coverage is often impossible. So you can over-rely on TDD. I’m more of a believer in system-testing under real conditions. FWIW]
It’s easy to get caught up in the heat of coding. Code, code, code! There is a mental switch required to write descriptive comments. It’s possible that switching gears like that is actually a good thing. There can be a highway hypnosis locked-in thing that ends up error-prone due to unnoticed fatigue and target fixation. Taking a breath to describe your work can improve your rhythm and endurance.
The counter-argument — and it is a valid one — is that early comments risk being outdated by changes to the code. The comments have to be updated along with the code. This creates one of the most prevalent instances of one of software design’s greatest challenges: synchronization.
Any time two things must be kept in sync — database field names and the use of those names in code, for example — there is the burden of having to update both if either changes.
There is no more common example of this than code and comments. The deadly part is that the comments can be wrong without actually impacting the code. (The code is always “right” — or, at least it always wins.) And yet the comments are the part programmers read first.
It’s when you read a comment and go, “Huh?!” that you might look closely at the code to see what it actually does. When cases like this are especially bad, it’s easy to wish there were no misleading comments in the first place.
There is also an argument about “self-documenting” code, and this argument also has a point. To the extent that the code itself can clearly describe what it’s doing, comments are superfluous. But this requires that the code be clear (Rule #1!). Key to this is that variable names be descriptive and obvious. It also usually implies short code bits with each bit doing a single obvious task.
Even code like this deserves a high-level descriptive comment to introduce it, to explain its purpose in life. (OTOH, a well-chosen object name can go a long way in describing what an object is for.)
But to be blunt, well-written self-documenting code is an advanced technique that takes most programmers years to achieve. Many never do; some programmers just never rise above Journeyman level (and some never above Novice). Good coding is partially a talent; you either have it or you don’t.
So obey Rule #4: Comment As You Go!
And consider commenting first, coding second. It’s a pretty good habit to develop.
I am going to pass this info along to my son. He graduates in May, and I’m sure some good habits will come in handy when he begins coding for a living.
I hope it does him well! Just the other day I was commiserating with a friend about badly written software — the modern tendency for bloated code because programmers are lazy and use 3rd party libraries rather than writing good tight code. So few craftsmen left!
This blog is about the craft of programming! (And about other programming stuff I like to natter on about here because it’s way too esoteric for my regular blog.)
(Nice to “see” you — hope all is well in Briggsland!)
I was always terrible at commenting. Even now, I like code to be self-commenting wherever possible. I’m getting better though.
Generally speaking, we all are (terrible at it), which is why it has to be a rule! The really important comments are the block ones, the ones that explain a block of code. Working with someone else’s code, sometimes you find an uncommented method or function stuck in the middle of things. Now you get to figure out what it does!
For me the rule was made clear long ago when I was going over some code I’d written years prior and found two uncommented small methods next to each other in the code (I use ordering of methods and functions as one more way to structure and document code). As far as I could tell, both methods seemed to do the same thing.
So I did the natural thing and refactored them into a single method…
Which instantly broke the code.
A closer look revealed there was a good reason for two nearly identical methods after all. Both were necessary. You can believe I made a pointed comment in the second method about not doing that again (and, more to the point, why). And learned a lesson about the value of comments!
Early on I went through a period of slavishly creating boilerplate block comments for every function. I’m sure you know the kind. There’s a space for inputs, outputs, side-effects, and whatever. Often what ends up going in these spaces is nothing or is obvious by looking at the function. Comments like that end up becoming a kind of noise, and they’re an energy-sucking kind of noise.
There is some value to them in a group that shares code, but it can still be soul-destroying overkill. Better to actually think about what’s important for some other programmer to know — it might be you in months or years when you’ve forgotten you ever wrote this.
The deeper lesson (and this would be worth a post on its own) is that there really is no such thing as “temporary” code. Once written, code takes on life and often lives far longer than expected. For that reason, it’s good habit to always write the clearest code you can.
Yes, it was that kind of standardized comment format that killed it for me. Most of it was so clearly pointless, it hid the fact that some of it was important!
Writing code has a lot in common with how we build society. Inserting pointless rules causes people to rebel against them. Temporary fixes become enshrined in law and custom for hundreds of years. Simple glitches have catastrophic consequences. And seemingly obvious improvements can have the reverse of the effect intended.
That is, perhaps, the nature of highly complex systems that evolve over time without a clear plan. The irony is that it’s been demonstrated that a clear plan doesn’t always result in the desired outcome.
Sometimes a system converges on a good outcome, but there is the problem of local maxima. A system can evolve “upwards” to a point of diminishing returns, but it’s possible a completely different solution might have a better outcome.
Exploring complex phase spaces is always a challenge! It’s a key factor that confounds AI.
Pingback: The Synchronization Problem | The Hard-Core Coder