Is Code Really Self-Documenting?
Learn how to better document your software by writing cleaner code, following some general commenting etiquette, and commenting more effectively to make you and your team more productive.
In my 20+ years of programming, I've encountered a near endless amount of opinions on everything from coding styles to programming paradigms to the great whitespace debate. Obviously, I have strong opinions on a number of these. But for me, the one that bothers me the most is this notion that "code is self-documenting." 😾
I know what you're probably thinking: "of course not all code is self-documenting, only well-written code is." I don't entirely disagree. I can generally look at someone else's code and understand exactly WHAT it is doing. However, often it's not obvious WHY they did it that way, or even why they did it in the first place. In my opinion, the programmer's intent (the WHY) is just as important as the HOW when it comes to properly documenting software.
So whether you agree with me or not, let's explore how to better document our software by writing cleaner code, following some general commenting etiquette, and commenting more effectively to make you and your team more productive. 👍
To comment, or not to comment 🤔
Before we dive in, let's just address the idea of commenting in the first place. Obviously, lots of opinions, but I personally always advise young programmers to, "comment, comment, comment, and then comment some more." The reason is typically two-fold. First, commenting can help them express what they're trying to accomplish, even if they're not using the best pattern to accomplish the task. This can turn code reviews into teachable moments. Second, it forces programmers to get into the habit of taking notes, which comes in handy when working on outside client projects, gathering release notes, and updating user documentation. Sure it can be a little messy at first, but I find that good programmers quickly figure out the right balance.
For more experienced programmers, I also advise that they comment sections of code that are particularly tricky or have been refactored and/or optimized. Unless you're held up in a bunker somewhere and you never plan on letting anyone else see your code, it's likely that another team member (perhaps one with weaker kung-fu 🥋) will end up working on it. Remember that time the new guy rewrote your recursive function because they had no idea how recursion works? I do, and apparently HR doesn't condone strangling people in the office. Who knew? 😬
Bottomline, yes, in a perfect world, comments should be mostly unnecessary. Unfortunately, turn on CNN and you'll realize we don't live in a perfect world, so we need to make the best of it. 🌎
A quick word on using annotation tools
I know, I know, these tools automagically document everything! Well, it's not really magic. First, you have to follow some very, very strict formatting rules which can be overwhelming for small teams. Second, if you are not writing an open source library with function references, then it can waste time and won't be particularly helpful for internal projects. These tools can be very effective for auto-generating documentation, but it takes quite a bit of effort to keep them updated. See JSDoc as an example.
Write better code 🤓
The first step to properly documenting software is to simply write clean and well-organized code. Here are a few tips to writing better code that is (almost) self-documenting.
Use meaningful names and conventions
Naming things is said to be one of the most difficult things in software development. That may have been true in the days when storage space and memory were limited, but any benefit of saving characters in modern systems is outweighed by the obscurity it creates. Whether you like to party like it's 1999 🎉 and use OOP 😂, you're a fan of imperative programming because you love endlessly hunting down bugs🐛, or you have been enlightened by the elegance of functional programming 🙌, descriptive and meaningful function/method and variable names are a must.
Consider this, if you write a function that adds two numbers together and then divides by 3, what's a better name for it:
javascriptfunction add(x,y) { return (x + y) / 3 }
OR
javascriptfunction addThenDivideByThree(x,y) { return (x + y) / 3 }
I would go with the latter because it clearly states what the function does and leaves little room for interpretation. The same holds true for variables:
let ordCt = 1
OR let orderCount = 1
As far as text case is concerned, follow standard practices of the language you're writing in. In JavaScript, we typically use lowerCamelCase for variables, functions and objects, and CAPS for constants.
Indent and leave space for readability
Some programming languages force you to properly indent code, but most do not. This means that programmers can argue "tab versus spaces" until the mini fridge runs out of Mountain Dew🥤. But regardless of your indentation inclination 😉, the bottom line is that proper spacing helps make code more readable. Code that is more readable is typically easier to understand and reason about. There are plenty of style guides on the interwebs that talk about indentation and whitespace, like here and here. Pick a style and stick to it. Your fellow programmers (and your future self) will thank you. 🤗
Write small, easily testable, pure functions
Complex functions that mutate variables and perform multiple actions are not only difficult to maintain, but they're also difficult to debug. A function should have a singular purpose and should be unit-testable. Pure functions, or functions that do not mutate state, are preferable because given the same input, they always return the same output. This means you can test each function independently to verify its functionality.
Writing functions this way also helps you follow the DRY principle (Don't Repeat Yourself). If you are constantly formatting a date the same way, writing a formatDate()
function will simplify code blocks that format the date, provide clear documentation to other programmers as to what it's doing, and ensure that all of your dates are formatted the same way. 🤘🏻
Commenting Etiquette 🧐🎩
Great, so now we have some well-written code with meaningful naming conventions, all properly indented (of course), and broken down into small, reusable parts with their own unit tests. However, as helpful as that might be, there are still some parts of the code that could use further explanations. Below is some common commenting etiquette to add needed context without bloating code with unnecessary artifacts.
"Explain WHY you did something, not WHAT you did" ~ Some Guy
This seems to be pretty common commenting advice. However, I'm going to have to go ahead and disagree with you slightly on this one. It's true that you don't want to insult your fellow programmers' intelligence (even if they deserve it), but there are circumstances where a little explanation can go a long way. Something like this would be pretty pointless (and insulting):
javascript// Add the values of x and y let total = x + y
However, I find something like this to be incredibly helpful:
javascript// Remove generated ID if not supplied in event R.when( R.compose(R.isNil,R.path(['event','id'])), R.assocPath(['info','id'],[]) )
The latter example technically tells us WHAT the code is doing, but it also gives us some context as to WHY we're doing what we're doing. It's certainly not necessary, however, a little reminder that ['info', 'id']
is the "generated ID" doesn't hurt.
Keep it professional
I once knew a programmer who would quote a Tori Amos song in the header of every script he wrote. Now you're probably thinking, "who the hell is Tori Amos?"👩🎤, but seriously, let's not do that. Sure it may seem funny, or clever, or rebellious even to stamp something with your own little signature. But for most people, you just wasted a few seconds of their life. Save the obscure and cryptic quotes for your social media feed, you'll get quicker validation. 🙅♂️
Also, comments should be helpful, not rude or laced with profanity 🤬. Calling out another team member's mistake OR disparaging the codebase, choice of technologies, or the company itself, can have unintended consequences. Potential acquirers performing due diligence may not find your unrelenting condemnation of Cassandra in your stack as hysterical as you do. 🤦🏻♂️
Save your future self and others from needless refactoring
I can't tell you how many times I've gone back to refactor a piece of code and realized that I (or another team member) had already tried that approach in the past. Do everyone a favor and share your knowledge and experience. I have this incessant need to refactor everything to be point-free 🤷♂️. Sometimes it just isn't possible, but that doesn't stop me from trying… over and over again. If I've banged my head against the wall for long enough, I'll eventually leave myself a note explaining WHY the code is written the way it is and WHY other approaches don't work as well or aren't as efficient. This saves me and others from seeing something that looks ugly and wasting more time on it.
Refactor poorly written code
Assuming no one left you a note explaining why there's a piece of spaghetti code stinking up your codebase 🍝, it's generally good practice to at least fix obvious formatting issues and mark the code for review. I know we're all super busy, but someone is going to have to eventually work on that code. If you see something that looks like code smell, don't just spray it with Fabreze. Be a hero and refactor it. 🏆
Make sure comments are CORRECT!
The trouble with comments that do not accurately reflect the code is that they may well be believed subconsciously, so the code itself is not examined critically. ~ The Elements of Programming Style by Brian W. Kernighan & P. J. Plauger
A strong argument for NOT commenting code is the laziness that it can instill in programmers. It's easy to take a comment at face value and assume that the code it is referencing actually does what the comment says it does. Comments can also quickly become out of date if they are not maintained properly. While I agree that this can (and most likely will) be a problem at some point, it's not enough of an issue to warrant removing comments altogether. Be a good commenting citizen and be sure to:
- Write accurate comments
- Update comments when you change code (or notice they are wrong)
- Remove old or unnecessary comments
Tips for writing better comments ✍
Sweet, so now we know how to write better code AND leave comments with manners that rival those of Oliver Bell Bunce himself (I totally had to Google that guy). Now we just have to get into the habit of actually writing comments and putting our newfound skills into practice. Here are three tips you can use for effective and efficient commenting.
Comment while you code
Commenting code can be time consuming (and frustrating) if you do it after the fact. Instead, leave comments as you write your code, even if they're just notes for you. You can always go back and clean up comments when you're wrapping up. It's much easier to delete unnecessary comments than it is to remember the significance of a block of code hours or days later. Worst case: You leave a bunch of extra comments that get caught during code reviews and merges. 👨🏻💻
Use TODOs and other keywords
This is one of my favorite things to do. My code is literally littered with TODO, IDEA, and REVIEW comments. Most IDEs will even summarize these comments for you so you can easily locate them in the code. Following this simple pattern can supercharge the value of your comments and help you and your team members more effectively manage your codebase. This shouldn't replace organizing TODOs and other tasks in Github or JIRA, but it is a great way to connect your code to other systems. Leaving comments with issue numbers or referencing an idea by linking back to your product management system can do wonders for keeping you organized.
Some common keyword examples:
javascript// TODO: Add error handling to connection method // FIXME: Fails on null values // CHANGED: Added third argument for boolean debug flag // IDEA: Cache user lookups for faster access // HACK: Using loop instead of a proper recursive function // NOTE: Items from the queue expire after 60 seconds, so be sure to timeout before that // REVIEW: Added new validation, double check that it isn't too restrictive // QUESTION: Should we move this set of functions to a separate library? // BUG: This renders incorrectly on IE11, issue #423 // TEMP: Set the AWS credentials for initial development tests (remove for production)
Leave notes for where you leave off
Context switching is one of the biggest challenges developers deal with every day. Getting interrupted by managers, Slack notifications, meetings, or salespeople 👨🏻💼(ugh!), can throw you off your game. It takes valuable time to get back into flow and tackle the task you were working on. Even if nobody ever bothered us and just let us code, at some point we'd still need to take a break or call it quits for the day.
You don't want to come back tomorrow and stare at your screen for 30 minutes trying to figure out what you were doing when you cut out of work last night to grab a burrito🌯, do you? Before you close your laptop and head off to Qdoba, leave yourself a short note that says what you were working on and what are your next steps. That way you can jump right back in before you finish your first cup of coffee. ☕
javascript// COMBAK: Was working on the new database connect() function and successfully connected to the test db. Next I need to work on error handling connection failures and then move on to the generic query() function.
Go Forth and Comment Your Code 👩🚀
Code commenting and style guides can be a sensitive subject. The most important thing is to strike the proper balance for your team. Having a codebase that's properly documented gets new hires up-to-speed more quickly, makes code more maintainable and can often eliminate a lot of annoying questions from junior devs! 😀
And that's it! Now you can go comment with gusto.
Did I miss something? Do you disagree with me? Let me know in the comments.