Click here to Skip to main content
14,217,582 members

Make Your Comments Matter

Rate this:
0.00 (No votes)
Please Sign up or sign in to vote.
0.00 (No votes)
29 May 2015CPOL
There are many different philosophies with regards to how source code should be commented. The gamut of these philosophies range from "Every single statement must have a comment." to "Comments are useless; avoid them at all costs!" I am not even going to attempt to explain the disparity of range.

There are many different philosophies with regards to how source code should be commented. The gamut of these philosophies range from "Every single statement must have a comment." to "Comments are useless; avoid them at all costs!" I am not even going to attempt to explain the disparity of range. However, I will attempt to address and suggest potential solutions the reasons that are often cited for these extreme approaches to commenting code.

To make a decision

I think that it is important to remind everyone that judicious decision making is required to write high-quality software. These are the types of decisions that cannot be easily made by following a flow-chart or a rote memorized rule. If it were that simple to make a decision, programmers would not be in such great demand.

As skilled professionals we often try to distill the rules to simpler pieces of advice to help less-experienced colleagues become better, faster. Unfortunately, information is often lost when we attempt to simplify our experiences into a neat set of rules.

Rules cannot always be reduced to a simple choice to help make the final decision.

Context

A general purpose rule tends to lack context. Context becomes very important once you move beyond the most trivial decisions. Events, statements, and ideas can not always be fully understood without the settings or circumstances involved.

A action such as self-defense cannot be determined without understanding the circumstances and settings involved, the context. Literal jokes are funny because a meaning is interpreted out of context. Context allows us to interpret meanings in forms that differ from their strict literal meaning.

Context adds clarity. Laws attempt to create rules for society and omit the context as much as possible to make the law more generally applicable. A law that is written with more specific terms is limited to fewer situations where it can be applied. This leaves loop-holes for criminals to take advantage of, and when interpreted most literally, innocent people may be punished.

Context cannot always provide the answer.

Everybody knows that!

Skills that once seemed impossible to learn, often becomes natural once we learn to master that skill. As a beginner, the slightest distraction could interrupt our train of thought. Added confusion tends to make decisions that are not familiar more difficult, which could lead to failure.

Skilled masters may not even consciously realize all of the decisions they are making as they perform their skill. Most average adult humans have mastered the skill of walking. Walking and talking, no problem (viral internet videos demonstrate that walking and texting is entirely different). Distracting a toddler learning to walk, or a child learning to ride a bike without training wheels often leads to a crash or fall.

My point is, as we become more skilled we can forget the things that we actually know and naturally do without consciously thinking about it. We may have spent thousands of hours learning and understanding something. Often we walk away with the well earned conclusion, and possibly a few memorable mistakes we made along the way. For most people, it is difficult to remember or imagine not knowing their mastered skill.

This leads to gaps in explanations, or assumptions about when a rule should be applied. Because, "that's just common knowledge" to the skilled master.

Expert's are not always the best people to create guidelines for making decisions. This is especially true when the guidelines are aimed at a group with a wide variation of skill level.

Where's the value?

This is a question that I learned to ask while I was practicing TDD and learning how to unit-test. I soon learned that I could ask this question in many different contexts. Asking and answering this question can help simplify the decision-making process.

For example, when considering a particular approach to solving a code problem, I try to compare the value of the solution to the amount of risk the solution may add. If the amount of risk out-weighs any perceived value, the decision becomes simple; find another way.

To answer this question will not make every decision simpler. However, it often can be used to reduce the set of choices that are available for you to select from.

This question alone has changed how I approach commenting source code. If a modification or addition of code does not add value, why does the code need to be modified. Keep in mind that deleting code can also add value to your program; in the form of improved readability, and less complexity.

Criticisms of Code Commenting

Just about all of the opinions that I have encountered for code comments are valid, for certain contexts. There is no opinion or rule, which I have heard that I believe is absolutely true for all scenarios. Let's take some time to consider these opinions.

Every line/statement must be commented

In all fairness, the commenting rules for this guideline originates from the aeronautics industry. I will not argue with a policy for code that is used in critical applications where major bodily harm or death could occur. I should also add organizations like NASA, where hundreds of millions of dollars is spent, then you have to wait for nine years for your space-craft to reach its destination. You want to be sure that it will work properly when it gets there.

Remember thought, extra comments are not necessarily a good thing. When they don't add value, sometimes they add clutter, other times they use up space, in the worse case they are misleading.

Comments do not get maintained along with the code

I have seen this many times. To make matters worse, it is not always easy to tell which item is incorrect, the comments or the code. For comments to provide value, they must be accurate. In a situation where the comments are completely inaccurate, there may be more value in simply removing the comments rather than trying to update them.

If this argument is considered when paired with the previous guideline, then there is definitely a strong case to avoid comments altogether. Prohibiting the use of a language feature also prevents the possibility of any benefit that feature could have provided. It does, however, require that your organization allows and expects your engineers to think.

The code should be clear and self-documenting

I agree with this statement.

However, descriptive names still have a limit to the amount of information that they can convey. At some point, long names become obnoxious. If not for the inconvenience of the extra typing, then for clutter that is added reducing the readability of the code.

This type of policy does not translate well to all programming paradigms. Structured and imperative languages will have the most success with this approach. Each step is part of a well-defined sequence. Furthermore, while the code sequence may be readable, the purpose for the entire sequence of commands may not be immediately clear. I tend to encounter this most frequently in monolithic functions.

Yes, these are comments... useless comments

Pertinent examples can illustrate this point much better than I could ever describe with words.

Cut-and-Paste to comment

C++

<span style="color: #0000ff;font-style: bold;">if</span> <span style="color: #008000;color: black; font-style: bold">(</span>SUCCESS <span style="color: #000040;">!</span><span style="color: #000080;">=</span> error<span style="color: #008000;color: black; font-style: bold">)</span>
<span style="color: #008000;color: black; font-style: bold">{</span>
  ...
<span style="color: #008000;color: black; font-style: bold">}</span> <span style="color: #666666;font-weight: 100; color: #008000">// if (SUCCESS != error)</span>

The context of the curly brace is already a good indicator that something is ending here. Many developer IDEs can help wiht the rest. For example, Ctrl + ] will jump between matching braces and parenthesis.

There is one exception where I actually do like ending a closing curly brace with a comment. That is at the end of a namespace. I find this is especially useful with deeply nested namespaces. This type of comment is valuable to me because I use it as a visual cue to help navigate namespace-scope.

Captain Obvious

Sky-diving without a parachute is a once in a lifetime experience!

C++

<span style="color: #666666;font-weight: 100; color: #008000">// Increment the index.</span>
index<span style="color: #000040;">++</span><span style="color: #008080;">;</span>

What is the purpose of this code... ?

My first instinct is to look at the header file.

C++

<span style="color: #666666;font-weight: 100; color: #008000">/// @SteamPunk.h</span>
<span style="color: #666666;font-weight: 100; color: #008000">/// This is the declaration of the SteamPunk class.</span>

I am an optimistic-realist, so I will try looking at the class declaration.

C++

<span style="color: #666666;font-weight: 100; color: #008000">/// The SteamPunk class specializes the BasicMachine class </span>
<span style="color: #666666;font-weight: 100; color: #008000">/// for the steam-punk functionality.</span>
<span style="color: #0000ff;">class</span> SteamPunk
  <span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> BasicMachine
<span style="color: #008000;color: black; font-style: bold">{</span>  
  ...
<span style="color: #008000;color: black; font-style: bold">}</span><span style="color: #008080;">;</span>

Fine. I will figure out what this class does on my own. "What happens when I set a mode and what modes can I set?"

C++

<span style="color: #666666;font-weight: 100; color: #008000">/// Sets the mode.</span>
<span style="color: #666666;font-weight: 100; color: #008000">/// @param[in] mode   - The value of the mode to set</span>
<span style="color: #0000ff;">void</span> SetMode<span style="color: #008000;color: black; font-style: bold">(</span><span style="color: #0000ff;">int</span> mode<span style="color: #008000;color: black; font-style: bold">)</span><span style="color: #008080;">;</span>

None of the comments from this section gave me any useful information. All that they did was duplicate explanations that were clear from the self-documented code. This is a situation where the comments were added solely to satisfy a code-quality checkbox.

We have always done it this way

Source control has been around for decades. However, before it was truly widespread there was a common practice of adding your edit history as a comment to the top of the source file. The way I see it, this is just more clutter. You can glean all of the history information from your version control tools and actually see the changes that were made by viewing a diff-comparison between the versions.

C++

<span style="color: #666666;font-weight: 100; color: #008000">/// 1/15/2002 - ABC: Initial implementation</span>
<span style="color: #666666;font-weight: 100; color: #008000">/// 3/28/2004 - ZED: Added that crazy feature</span>
<span style="color: #666666;font-weight: 100; color: #008000">/// 4/02/2004 - TEL: Fixed ZED's crazy feature</span>
<span style="color: #666666;font-weight: 100; color: #008000">/// 5/14/2007 - PJT: Upgraded the logging system</span>
<span style="color: #666666;font-weight: 100; color: #008000">/// 2/28/2010 - ABC: Fixed that memory leak</span>
<span style="color: #666666;font-weight: 100; color: #008000">/// 8/02/2011 - LOG: Changed to another logging system</span>

Increase the value of your code with useful comments

Again! Remember to ask what value are you providing with each modification. If the comment you are about to write can easily be gleaned from reading the next few lines, your comment becomes less valuable.

Comments should be used to annotate and document your source code in ways that are not possible with code alone.

Document purpose

If you are familiar with the domain for which your program is being written, the purpose of an object may be obvious to you. However, new programmers working with the same code, may not possess the correct vocabulary to work effectively with the source code. Moreover, it could slow down or even prevent other developers from being capable of working within your code.

Document usage

Self-documenting code does not always provide enough information for someone to appropriately use your class or function. The meaning of a function name could be misinterpreted, or the purpose of each input variable may be ambiguous.

There are a number of automated tools that extract specially formatted comments to generate external documentation that you can use for reference independently of your code. Some of these tools are capable of providing so much more than documentation. For instance, Doxygen can generate relationship diagrams for your class definitions. You can also incorporate Mark-down definitions directly in your code to generate up-to-date design documentation.

Clarify intentions

Sometimes the intended purpose for a sequence of logic is just not obvious (probably more than sometimes). When it is not obvious, summarize your intentions. If you make a mistake in your code, it may make it easier to integrate that test for a corner case that you missed if it is clear what the code was intended to do.

Document rationale

We build poor quality code one statement at a time. Later we may try to repent for our sins and clean up the code. However, sometimes there is no reason that is clearly apparent for why code is structured the way it is.

Perhaps atomic operations could replace more expensive synchronization objects like a mutex, if the statements were re-ordered. Since the order of the statements is important, documenting this code structure would be valuable for any future developer that works with this code.

Some times similar statements placed in proximity of each other improves the readability of the code. The prettiest code in the world becomes a useless program if it does not function properly.

Summarize blocks of behavior

I believe one of the most valuable places to comment code is at the beginning of a statement block. Even if the block contains 10 lines of the simple statements that can be easily followed, summarize what the 10 lines accomplish.

If the comments can be trusted, this allows the reader to combine that collection of logic into one abstract notion while reading the rest of the code.

What does the snippet of code in the example below do?

C++

<span style="color: #666666;font-weight: 100; color: #008000">// Creates a linear gradient at a specified angle</span>
<span style="color: #0000ff;">bool</span> AngularGradient<span style="color: #008000;color: black; font-style: bold">(</span>...<span style="color: #008000;color: black; font-style: bold">)</span>
<span style="color: #008000;color: black; font-style: bold">{</span>
...
  <span style="color: #0000ff;font-style: bold;">if</span> <span style="color: #008000;color: black; font-style: bold">(</span><span style="color: #0000dd;color: red">0</span> <span style="color: #000080;">==</span> <span style="color: #008000;color: black; font-style: bold">(</span>offset <span style="color: #000040;">%</span> <span style="color: #0000dd;color: red">2</span><span style="color: #008000;color: black; font-style: bold">)</span><span style="color: #008000;color: black; font-style: bold">)</span>
  <span style="color: #008000;color: black; font-style: bold">{</span>
    std<span style="color: #008080;">::</span><span style="color: #007788;">swap</span><span style="color: #008000;color: black; font-style: bold">(</span>cC<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">0</span><span style="color: #008000;color: black; font-style: bold">]</span>, cC<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">1</span><span style="color: #008000;color: black; font-style: bold">]</span><span style="color: #008000;color: black; font-style: bold">)</span><span style="color: #008080;">;</span>
    std<span style="color: #008080;">::</span><span style="color: #007788;">swap</span><span style="color: #008000;color: black; font-style: bold">(</span>alpha<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">0</span><span style="color: #008000;color: black; font-style: bold">]</span>, alpha<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">1</span><span style="color: #008000;color: black; font-style: bold">]</span><span style="color: #008000;color: black; font-style: bold">)</span><span style="color: #008080;">;</span>
  <span style="color: #008000;color: black; font-style: bold">}</span>
 
  offset <span style="color: #000080;">=</span> std<span style="color: #008080;">::</span><span style="color: #0000dd;">abs</span><span style="color: #008000;color: black; font-style: bold">(</span>offset <span style="color: #000040;">-</span> <span style="color: #0000dd;color: red">4</span><span style="color: #008000;color: black; font-style: bold">)</span> <span style="color: #000040;">%</span> <span style="color: #0000dd;color: red">4</span><span style="color: #008080;">;</span>
  COLORREF clr<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">4</span><span style="color: #008000;color: black; font-style: bold">]</span> <span style="color: #000080;">=</span> <span style="color: #008000;color: black; font-style: bold">{</span> c1, cC<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">0</span><span style="color: #008000;color: black; font-style: bold">]</span>, c2, cC<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">1</span><span style="color: #008000;color: black; font-style: bold">]</span><span style="color: #008000;color: black; font-style: bold">}</span><span style="color: #008080;">;</span>
  std<span style="color: #008080;">::</span><span style="color: #007788;">rotate</span><span style="color: #008000;color: black; font-style: bold">(</span>clr, clr <span style="color: #000040;">+</span> offset, clr <span style="color: #000040;">+</span> <span style="color: #0000dd;color: red">4</span><span style="color: #008000;color: black; font-style: bold">)</span><span style="color: #008080;">;</span>
 
  USHORT   alphaV<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">4</span><span style="color: #008000;color: black; font-style: bold">]</span> <span style="color: #000080;">=</span> <span style="color: #008000;color: black; font-style: bold">{</span> alpha1, alpha<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">0</span><span style="color: #008000;color: black; font-style: bold">]</span>, alpha2, alpha<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">1</span><span style="color: #008000;color: black; font-style: bold">]</span><span style="color: #008000;color: black; font-style: bold">}</span><span style="color: #008080;">;</span>
  std<span style="color: #008080;">::</span><span style="color: #007788;">rotate</span><span style="color: #008000;color: black; font-style: bold">(</span>alphaV, alphaV <span style="color: #000040;">+</span> offset, alphaV <span style="color: #000040;">+</span> <span style="color: #0000dd;color: red">4</span><span style="color: #008000;color: black; font-style: bold">)</span><span style="color: #008080;">;</span>
...
<span style="color: #008000;color: black; font-style: bold">}</span>

Here is the comment that I have placed above this section of code. It summarizes an entire block of logic as well as documents my rationale and intent of the behavior for the code:

C++

<span style="color: #0000ff;">bool</span> AngularGradient<span style="color: #008000;color: black; font-style: bold">(</span>...<span style="color: #008000;color: black; font-style: bold">)</span>
<span style="color: #008000;color: black; font-style: bold">{</span>
...
  <span style="color: #666666;font-weight: 100; color: #008000">// As the angle's start point changes quadrants, the colors will need to</span>
  <span style="color: #666666;font-weight: 100; color: #008000">// rotate around the vertices to match the starting position.</span>
  <span style="color: #0000ff;font-style: bold;">if</span> <span style="color: #008000;color: black; font-style: bold">(</span><span style="color: #0000dd;color: red">0</span> <span style="color: #000080;">==</span> <span style="color: #008000;color: black; font-style: bold">(</span>offset <span style="color: #000040;">%</span> <span style="color: #0000dd;color: red">2</span><span style="color: #008000;color: black; font-style: bold">)</span><span style="color: #008000;color: black; font-style: bold">)</span>
  <span style="color: #008000;color: black; font-style: bold">{</span>
    std<span style="color: #008080;">::</span><span style="color: #007788;">swap</span><span style="color: #008000;color: black; font-style: bold">(</span>cC<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">0</span><span style="color: #008000;color: black; font-style: bold">]</span>, cC<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">1</span><span style="color: #008000;color: black; font-style: bold">]</span><span style="color: #008000;color: black; font-style: bold">)</span><span style="color: #008080;">;</span>
    std<span style="color: #008080;">::</span><span style="color: #007788;">swap</span><span style="color: #008000;color: black; font-style: bold">(</span>alpha<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">0</span><span style="color: #008000;color: black; font-style: bold">]</span>, alpha<span style="color: #008000;color: black; font-style: bold">[</span><span style="color: #0000dd;color: red">1</span><span style="color: #008000;color: black; font-style: bold">]</span><span style="color: #008000;color: black; font-style: bold">)</span><span style="color: #008080;">;</span>
  <span style="color: #008000;color: black; font-style: bold">}</span>
  <span style="color: #666666;font-weight: 100; color: #008000">// Remainder of snippet excluded</span>
  ...
<span style="color: #008000;color: black; font-style: bold">}</span>

The code snippet above is from one of my favorite articles discussing the AlphaBlend and GradientFill functions from the Win32 API. I posted it at CodeProject.com a few years ago. Here is a link if you are interested in reading more from that article.Win32 Image Composition[^]

Summary

Programming source code is an attempt to express an abstract thought into a concrete idea. The intended audience for this expressed idea is usually a compiler or interpreter. This can lead to some very cryptic commands, because all that matters in this context is following discrete rules of the programming language. A secondary audience are the humans that will continue to develop and maintain the program.

Code comments are a way for us to express intent, rationale for a decision, instructions of usage, or just a simple reminder. Comments can provide value. However, value is not usually achieved by blindly following a set of rules in a coding guideline. Carefully thought through and conscious decision-making is generally required. As is the case for the source code itself.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Paul M Watt
Engineer
United States United States
I am a software architect and I have been developing software for nearly two decades. Over the years I have learned to value maintainable solutions first. This has allowed me to adapt my projects to meet the challenges that inevitably appear during development. I use the most beneficial short-term achievements to drive the software I develop towards a long-term vision.

C++ is my strongest language. However, I have also used x86 ASM, ARM ASM, C, C#, JAVA, Python, and JavaScript to solve programming problems. I have worked in a variety of industries throughout my career, which include:
• Manufacturing
• Consumer Products
• Virtualization
• Computer Infrastructure Management
• DoD Contracting

My experience spans these hardware types and operating systems:
• Desktop
o Windows (Full-stack: GUI, Application, Service, Kernel Driver)
o Linux (Application, Daemon)
• Mobile Devices
o Windows CE / Windows Phone
o Linux
• Embedded Devices
o VxWorks (RTOS)
o Greenhills Linux
o Embedded Windows XP

I am a Mentor and frequent contributor to CodeProject.com with tutorial articles that teach others about the inner workings of the Windows APIs.

I am the creator of an open source project on GitHub called Alchemy[^], which is an open-source compile-time data serialization library.

I maintain my own repository and blog at CodeOfTheDamned.com/[^], because code maintenance does not have to be a living hell.

Comments and Discussions

 
-- There are no messages in this forum --
Technical Blog
Posted 29 May 2015

Tagged as

Stats

3.3K views