Let's look at the following piece of .ascx.cs code [1]:
///<summary>
/// 0 - parentId,
/// 1 – postbackUrl,
/// 2 – container,
/// 3 – default uploader
///</summary>
private const string template = @"{0} = new Uploader ('{0}','{1}','{2}','{3}');";
...
string script = string.Format(template, ClientID, ...);
AddJavascript(script);
It’s one of those web development oldies: combine some parameters on
the server side and a javascript object gets created in a browser. The
problem is that if you need to pass more than 2-3 parameters the code
becomes less clean. It gets difficult to tell what every single
parameter means on the javascript side of things. That’s why the author
needed a comment in the first place.
One of possible solutions is to refactor the original code using a
javascript object. The C# string that initializes an object would
contain all the information itself, so we wouldn’t need the comment
anymore.
private const string template =
@"{0} = new Uploader (
{{
parentId: '{0}',
postbackUrl: '{1}',
container: '{2}',
defaultUploader: '{3}'
}});";
Object initialization pretty
much explains itself. If we had a comment, adding a new parameter would mean updating the comment as well – without it we just update the template.
This raises a few questions
though. Why do we use comments at all? Do they provide information that
cannot be easily obtained otherwise? Or are they just a deodorant that
attempts to hide bad code smell? How can we tell pertinent ones from
useless ones?
Dark side of things.
How often do you see these vapid green clarifications like "Open the file" next to a call to File.Open, or "Saving the changes" next to a TransactionScope.Complete?
Perhaps the author was paid by line of code and comments were included.
Or he was not sure that people reading the code would be smart enough
to understand. Or he was not sure the code would do what it should do,
and thus added some emphasis. But most likely, such comments appear in
spaghetti code places, where the code is so clumsy that you start
adding comments next to some important parts of the code, in order to
spot them easily in such a mess.
It’s critical to have as less obvious comments as possible.
Most likely they are an indicator of bad code because usually used in
places where adding a comment looks easier than changing the code.
Someone may raise a question –
ok, those comments are obvious, but if the code is clean do they make
any harm, why should I strip them off? And the problem is that they
just put additional noise. Code has some noise of itself, variables and
methods and such, not everything is named in the perfect way... and if
a comment is just restating the same thing that is already in the code,
it’s better to strip it off.
Because
every time you add a comment, you take the responsibility of supporting
it. Maintenance burden. If your method parses an XML and a comment
states that you’re parsing an XML, and then later on you add support
for CSV files, you need to update the comment. Not a big deal
obviously, but it has one major implication: you may be imposing this
mainenance responsibility upon someone else who will maintain your code. Usually
it means the comment will be removed if the associated code gets
changed (in a more deserted scenario the comment would become confusing
and could potentially lead to incorrect assumptions about the code
flow).
The
remedy is to write self-documenting code. Easy to say, difficult to do:
it implies a background thread in your mind that is continuously
assessing the readability of the code. It claims constant refactoring.
It means to have something elusive but very important: good taste.
Good taste does not correlate with experience as much as people usually
assume. While it’s likely the person with little experience would write
clumsy code, the opposite is far from being always true. I've seen a
fair amount of clumsy code from developers with more than 5 years of
coding experience. Will tell you even more, but please keep it secret:
I’m writing clumsy code myself. It is so disgusting that I have to
refactor it every time before I check in, to keep the team morale high.
Clumsy code is one of the
reasons for comments. They are not "Open the file"-like, but can be
eliminated as well. Definitely, with unnatural design or perverted code
flow it’s easier to add a comment rather than to refactor the solution.
But refactoring is the right way.
Good riddance to bad rubbish. Code flow should be clear from just looking at the code, not from reading the comments. It's a plod but the end result worth it – in
fact, it’s a choice between having bad code with comments versus having
good code with a few comments or with no comments at all.
I’m not going to give any real code samples because the range of ideas
is so wide and the approaches are so diverse. But some general
suggestions for making a comment redundant might be:
• Introducing local variables that express the comment idea.
• Creating sub-routines with names that express the comment idea.
Bright side of things.
Time to say 'but'.
Obviously I don’t mean developers are scuba divers amidst the green
algae of useless commentary. Sometimes comments are good and sometimes
they are even necessary. (Not to mention here large organizations and
their peremptory demands, commenting code for the sake of commenting code.)
Comments help the team to use the code. If properly written, they
appear in IntelliSense tooltips. They explain the need for a particular
type and explain the reason why a particular design or coding approach
was chosen.
Of course, you better off writing them against stable code. One of the
reasons why XP suggests avoiding comments is that the code base is
being changed so frequently that there’s no need to have an additional
burden of maintenance. For those who treat every agile statement as a
guide to action but still want to write comments, here’s good news:
Staged Delivery considers comments more seriously [2].
Comments work along with self-documenting code, just on a higher level. Code does its best to explain how
a particular solution works, so that you don't need to spend a lot of
time to understand the code flow. On the other hand, comments help you
to understand why the code works in the way it works.
Comments for classes, structs and enums explain the need for them and
clarify their place within code base. Those for interfaces outline
design contracts and specify input/output restrictions and fault
conditions. Those that live directly in code explain the reasons for
specific coding solutions and emphasize innocent blocks that have a
huge impact. Also, comments help you to comment out the unused code if
you’re afraid to delete it (kidding!).
Usage
scenarios. Design decisions. Unobvious workarounds and bug fixes.
Performance hints. Fault conditions for methods. Recovery plans, if
any. Thread safety. Any of those can (um, should) go to comments.
Forget about the obvious stuff anyone can get from just looking at the
code. Saying "Pesky customer class" against PeskyCustomer class is not going to endear you to anyone, let alone pesky customers.
Use complete sentences that will be clear to the rest of the team but avoid verbosity: comments should be as detailed as needed but as brief as possible.
And don't forget that everything becomes inaccurate over time. Trust
comments but verify, and find a minute or two to fix incorrect ones
when you come across them.
Happy commenting!
Footnotes.
[1] - This text is based on a presentation I've made a while ago. It's not a complete snapshot though, but pretty close to it. The slides are available on this site, or directly on authorStream.
[2] - There's a nice 'Staged Delivery vs. XP/Scrum' comparison done by Roy Osherove (well, based on a Juval Lowy class, so credit for both I guess).