“What on earth does this do?”
“Why wasn’t this a ticket?”
Been there? If you’re anything like most engineers, it’s safe to say this happens to you a lot.
The most obvious and immediate benefit of writing good comments is that they make code easier for others to understand.
But when we nail our code commenting best practices (which include when to and when not to write comments), we can unlock even more impactful, long-lasting benefits like:
On the other hand, when we don’t embrace good practices, code comments can have the opposite effect. After all, code comments are an unstructured way of storing information. They’re not easily searchable or visible beyond looking at the code. This makes them a poor alternative for proper documentation or issue tracking. Combined with the code quality nosedive and velocity issues they cause, poor commenting leads to mounting problems, from huge learning curves for new engineers to invisible backlogs of tech debt.
In this complete guide to writing meaningful code comments, we’ll:
Some say that if a piece of code is written well, we can do away with comments altogether because that code’s purpose will be obvious. As John Ousterhout says, this is a delicious myth.
Comments can indeed be used to plaster over poor or unclear code. But the bottom line is that various kinds of information simply can’t be represented in code.
Contextual comments are used to describe any purpose, intent or feature of code that isn’t obvious by looking at it. There are two primary types of contextual comments.
The first type are module-level comments. These describe the purpose of classes, functions and methods.
These comments might include…
The second type are logic comments. These explain code in context, wherever they are needed.
We should use them sparingly. The last thing your team needs are comments that explain things which are already obvious by glancing at the code!
Well-placed comments save development time and prevent engineers from misunderstanding the purpose or context of a code snippet. When we misunderstand code, it’s possible to introduce inefficiency, unnecessary complexity and bugs. These harm code quality and velocity, particularly when it happens routinely.
✅ When to use them:
For example, it’s unclear whether the `start` and an `end` parameters are inclusive or exclusive. A comment would save readers from working it out.
Fewer comments often mean more readable code. If somebody sees a comment, they’ll know it’s incisive and valuable.
❌ When to avoid them:
Here’s an important point – if engineers are writing tonnes of comments to explain complexity, that could be a code smell.
An engineering practice your team should adopt is consistently documenting unnecessarily complex code as issues. This avoids accumulating serious tech debt that can be hard to pay back. If you’re not already using a tool for this, try Jira or Linear for app-based issue tracking. Or, try Stepsize to track and manage tech debt from your code editor.
TODOs and FIXMEs are everywhere. But they come with a substantial cost.
TODOs are a great single-player tool... They are great for temporarily dumping thoughts so you can focus on what you’re doing. TODOs are definitely better than having a list on the side, because they have context.
…But a terrible multiplayer tool. When TODOs make it to `main`, you end up with a never-ending, invisible backlog which isn’t actionable.
These bad comment practices are a direct path to spiralling tech debt and declining code quality.
Use TODOs to aid your personal code development process. Never push TODOs to `main`.
If you have TODOs in your codebase, use Stepsize’s issue management tool to convert them into issues without leaving the IDE. The tool will also surface TODOs in PRs, where you can turn them into issues.
The best code comments are the ones you don’t need. The best comments can’t be replaced by code.
Check out this example we borrow from Jef Raskin:
/* A binary search turned out to be slower than the Boyer-Moore algorithm for the data sets of interest, thus we have used the more complex, but faster method even though this problem does not at first seem amenable to a string search technique. */
Code should be written as simply as possible. The best comments explain unavoidable complexity. For example, we might add a comment explaining the code's business application or the rationale for choosing a particular algorithm.
Code tells you how. Comments tell you why.
TODOs should only be single-player. TODOs that get pushed result in a never-ending, invisible backlog which isn’t actionable.
If you’re pushing a TODO, you should be creating an issue instead. When leaving TODOs in a codebase is a team-wide habit, it leads to spiralling tech debt.
Use a tool like Stepsize to manage issues without leaving your codebase. Stepsize integrates with platforms like Jira and Linear if you want it to. The VSCode extension is here, and the Jetbrains extension is here. This tool will enable you to:
Documenting your conventions helps keep your comments consistent. This forces teams to decide what will be commented, and what the format will be.
You might be programming in a language with a document compilation tool, such as Javadoc for Java, godoc for Go! Or Doxygen for C++. Make use of these – even though they’re not perfect, the structural benefits outweigh the drawbacks.
Choose tools and practices for managing issues that are intuitive for individual engineers, and that delivers value to the team. Are you disallowing TODOs in `main` or having a policy on how issues are documented? Make sure this is universally understood.
Once good practices become habits across a team, they become an effortless contribution towards shipping faster, better code and managing tech debt properly.
Enshrining your principles pays back dividends when engineers join or leave your team, too.
Code commenting isn’t just about knowing how to write good comments. It’s also about knowing when not to – and what to do instead.
When we get this right, we don’t just make it easier for others to understand. We:
Firstly, when we write good comments, we constantly surface essential information about code features and architecture decisions.
Secondly, when we avoid commenting about code problems and create issues instead, we ensure issues are actionable and visible. This promotes good tech debt management practices, which enable your team to fix tech debt issues.
Optimise the process of documenting and fixing tech debt issues in your team by using Stepsize’s issue management tool. It allows you to: