Writing Self-Documenting Code & Good Code Commenting Practices

Introduction

We all know that writing good code that is efficient, stable and secure can be a challenging task. Because of this, as developers, we are tempted to put things like comments, documentation and readability to the wayside. Often we say things like, "I'll do it when I have time" or "We'll create a tech debt task for that". But often, we fail to realize that comments, readability, and documentation are all crucial parts of those things we mentioned first (efficiency, stability, and security). Let's dive into some deeper topics to quantify this claim.

3 Reasons Why (Not)

Why do developers not properly comment their code? Here are some popular excuses that I have come across in my tenure...

"I'm the only one working in this code base."

Just like with version control, good comments are still important even if you are the only one working on the project. We all have faulty memories, and revisiting your code many moons later can be aggravating if you are diving into your code wondering, "What on earth was I doing here?"

"I don't have time."

Just like we have learned about how important unit tests are, the same is true for comments/documentation. And when you make these things a part of your coding routine, they become second nature, and in fact can often improve coding time! That is, having clear comments throughout your code as you develop can often keep you on track and better focused on the task you are trying to accomplish.

"The code is self-documenting!"

No, it's not!

This one is my personal favorites and it is the easiest to argue against. Developers will say time and time again that if code is written well enough, it should not need comments. This is the biggest fallacy of them all! Yes, for very small code bases you can likely get away with minimal comments. But for most projects, this just is not the case. Good comments and documentation throughout will provide a great service to those coders (including the original author) revisiting the code months or years later.

Don't Assume

When we comment improperly or not at all, it is often due to the assumptions we make about the next person who looks at our code. We may think it is just going to be us. We might also assume our code won't be around in five years, or that it is written so well that it will be instantly understandable by even the greenest of coders. These are dangerous assumptions that can lead to bad code over time. Here are some other things that you should NOT assume when coding...

Don't Assume Your Audience

The next person who looks at your code may not be you. It may be a lousy developer! It may not be a developer at all! Write your code and comments in such a way that someone who is a coder, but does not know the language, can understand what your code is doing. Also ensure your comments are for someone who is NOT a coder so that they can at least gain a high-level understanding of what your code/module/function INTENDS to do. More on intent in the "Criminal Intent" section below.

Don't Assume The Future

Your code might be awesome, it might be terrible. Only time will usually tell. Your code might get replaced in a few years. It might need a major refactor because requirements have changed. It might outlive you!! Your comments, code and documentation must be easy to understand for pretty much anyone, at least on a high level.

Criminal Intent

When coding, knowing the intent is important. But it is JUST as important to know the intent and purpose after the fact. Comments that just say technically what the code does are not nearly as helpful as intent. Instead, follow these guidelines for comments...

  • Every function, at minimum, should indicate its purpose (see Mark it Up! below).
  • Each task or operation within the code needs to have at least a brief phrase indicating intent.
    • That is to say, you do not need comments for every single line of code. But rather, every "chunk" of code that does something toward the end goal of the function.
  • Functions and Classes should use mark up comments (see Mark It Up! below).

Ideally, as a developer, I would encourage you to write these comments BEFORE you write a line of code. This is also true even when you are coding out a particular function. For example, something like this would be appropriate to start with...

/**
 * Filters names containing the word "bob" in them.  The remaining 
 * @param {string[]} names 
 * Names to sort.
 * @returns {string[]} 
 * Resulting list of filtered and sorted names.
 */
function filterAndSortNames(names) {
    // TODO: Create a new empty string array
    // TODO: Loop through the names and ignore
    // TODO: Sort and return names
}

Without writing a line of code, you have a good idea of the INTENT of it. Next, fill it in with code (and modify the TODO comments as needed)...

/**
 * Filters names containing the word "bob" in them.  The remaining 
 * @param {string[]} names 
 * Names to sort.
 * @returns {string[]} 
 * Resulting list of filtered and sorted names.
 */
function filterAndSortNames(names) {
    return names
        // Filter any names that include "bob" in them
        .filter(i => {
            // Make lowercase so we can find "Bob", "boB", etc.
            let itemAsLowercase = i.toLowerCase();
            // Remove from returned results if name has "bob" anywhere
            return !itemAsLowercase.includes('bob');
        })
        // Return results sorted in ascending ASCII order
        .sort();
}

This example is interesting, because our final comments are quite a bit different from our original intent. Looks like the developer discovered built-in Javascript functions to do this much easier. Still, our final comments show intent so that even without any code, we see what the developer was trying to accomplish. I mean, without the comments, I may not understand what the point of the `toLowerCase` is since we just care about "bob". Or I might not know how it's sorted ascending, etc.

This is obviously a very rudimentary example, but more complex code would benefit far greater from this process.

Mark it Up!

Just about every modern language includes support for markup within comments. The above example, you'll see, has JSDoc markup embedded within the function comments. This markup not only eases the burden of developers when calling that function (most IDEs will show these when you start typing out the call), but it also provides a way to generate documentation that exists outside of the code. When maintained well, this documentation can be reviewed independent of your code. That documentation can then be reviewed by someone, and they should be able to gain at least a high-level understand of how your code works.

Markup is vital to any project, and at minimum, should be used on all functions and classes within your code.

Conclusion

Being proactive about commenting and documenting can greatly help in your development process. When commenting properly, it will in fact increase your efficiency, stability and security of your code because you have now made your code easier to read and maintain for yourself and future developers.

Introducing the JBS Quick Launch Lab!

FREE 1/2 Day Assessment

Quantify what it will take to implement your next big idea! Our intensive 1/2 day session will deliver tangible timelines, costs, high-level requirements, and recommend architectures that will work best, and all for FREE. Let JBS show you why over 20 years of experience matters.
Yes, I'd Like A FREE Assessment