Comments in Code are a Sign of Failure, Not Clarity
In the world of software development, there is a long-standing myth passed down from CS 101 professors to junior developers: “Good code is well-commented code.” For decades, we have been told that a proliferation of green text in our IDE is a sign of a diligent, thoughtful programmer. However, as software engineering has matured into a craft, the industry’s leaders—most notably Robert C. Martin in Clean Code—have proposed a more radical truth: Comments are often a sign of failure.
The core of this argument is not that communication is bad, but that comments are a secondary, often unreliable way of communicating. When a developer writes a comment, they are essentially admitting they were unable to express their intent through the code itself. If the code were clear enough, the comment would be redundant. In this guide, we will explore why comments can be a symptom of “code smell” and how to achieve true clarity through self-documenting code.
The Paradox of the Comment: Why They Lie
The primary issue with comments is that they are not executable. Compilers and interpreters ignore them entirely. Because they have no impact on the functionality of the program, they are frequently neglected during refactoring. This leads to the most dangerous element in any codebase: the stale comment.
- The Lie of Accuracy: Code evolves. Requirements change, bugs are fixed, and logic is updated. If a developer updates the code but forgets to update the comment above it, that comment becomes a lie. A future developer reading that comment will be misled, leading to wasted hours of debugging.
- The Noise Factor: When a file is 50% comments, the signal-to-noise ratio drops. Developers begin to “skim” the file, often missing critical logic because it is buried under paragraphs of outdated explanations.
- The Redundant Echo: Many developers write comments that simply repeat what the code says. Writing
// increment i by 1abovei++;provides zero value and only clutters the workspace.
Comments as a “Band-Aid” for Bad Code
Think of a comment as an apology. When you write a comment to explain a complex block of logic, you are saying, “I’m sorry I couldn’t make this code simple enough for you to understand just by reading it.” Instead of spending time writing the apology, a professional developer should spend that time fixing the underlying issue.
Consider a complex conditional statement: if (employee.flags & 0x80 && employee.age > 65). A developer might add a comment: // Check if employee is eligible for full retirement benefits. While helpful in the moment, it hides the fact that the code itself is cryptic. A better approach is to refactor the code to: if (employee.isEligibleForFullRetirement()). By doing this, the comment becomes unnecessary because the code expresses its own intent.
The Path to Self-Documenting Code
The goal of modern software craftsmanship is “self-documenting code.” This is code that reveals its intent, its logic, and its structure through naming and organization rather than through external annotations. Here is how you can achieve clarity without the crutch of comments.
1. Meaningful Naming
Variables, functions, and classes should have names that are descriptive enough to replace a comment. A variable named d requires a comment like // days since last login. A variable named daysSinceLastLogin requires no explanation. If you find yourself needing to explain what a variable is, you haven’t named it correctly yet.
2. Small, Single-Purpose Functions
Long functions are the primary breeding ground for comments. If a function is 100 lines long, you’ll likely feel the need to place comments every 10 lines to mark “Step 1,” “Step 2,” and so on. This is a sign that the function should be broken down. Each of those “steps” should be its own function with a descriptive name. When you look at the master function, it should read like a high-level summary of the process.
3. Explain the “Why,” Not the “How”
If you absolutely must write a comment, focus on the why. The code already tells us how the computer is doing something. What the code can’t always tell us is the business context or the reason for a non-obvious workaround.
- Bad Comment:
// Use a regex to strip non-alphanumeric characters.(The code shows this). - Good Comment:
// We use a regex here because the legacy API fails if it receives special characters.(This explains a constraint not visible in the logic).
The Hidden Cost of Maintenance
Every line of code is a liability, and that includes comments. When you write a comment, you are creating a new piece of data that must be maintained. If your team has a strict “comment everything” policy, you are doubling the surface area of your maintenance task. In a fast-paced environment, this is rarely sustainable.
Furthermore, comments can lead to “mental laziness.” When a developer knows they can just explain a messy hack with a comment, they are less likely to put in the effort to find a cleaner, more elegant solution. This leads to a gradual accumulation of technical debt that eventually makes the codebase impossible to manage.
When are Comments Actually Necessary?
To say that all comments are a failure is an exaggeration, though it is a useful mindset for improvement. There are specific scenarios where comments are not only acceptable but essential:
Legal and Copyright Information
Most enterprise environments require a header comment for licensing, copyright, and authorship. This is a bureaucratic necessity that has nothing to do with code logic.
Warning of Consequences
Sometimes, a piece of code looks like it should be refactored, but doing so would cause a catastrophic failure in an obscure corner of the system. // DO NOT change this timeout value; the third-party hardware requires exactly 500ms to initialize. This is a valuable warning that the code cannot convey on its own.
TODOs and Tracking
Short-term comments like // TODO: Refactor this after the API migration next week are useful for team coordination. However, these should be treated as debt and cleared as soon as possible.
Public APIs and Documentation
If you are writing a library for other developers (like a public NuGet or NPM package), comments like JSDoc or JavaDoc are vital. These comments are used by IDEs to provide intellisense and generate external documentation pages. In this context, they serve as the user manual for your product.
Refactoring: The Real Solution to “Clarity”
The next time you find your hand hovering over the forward-slash keys to write a comment, stop and ask yourself: “How can I change this code so that this comment is no longer needed?”
- If you are explaining a complex expression, extract it into a variable with a clear name.
- If you are explaining a block of code, extract it into a method.
- If you are explaining the sequence of events, rename your methods to better reflect the workflow.
- If you are explaining a “magic number,” replace it with a named constant.
By shifting your focus from “explaining the mess” to “cleaning the mess,” you create a codebase that is inherently more readable, more maintainable, and less prone to the “bit rot” that plagues heavily-commented legacy systems.
Conclusion: Code Should Speak for Itself
The ultimate goal of any software developer should be to write code that is so clear, so expressive, and so well-structured that comments feel like an intrusion. While comments aren’t “evil,” they are often a “smell” indicating that the code is not as clear as it could be.
Professionalism in coding isn’t about how much you explain; it’s about how much you simplify. By prioritizing clean naming, small functions, and intuitive logic, you eliminate the need for apologies in your code. Remember: The best comment is the one you didn’t have to write because the code was already perfect.
