Somewhere around 2015, the software industry collectively decided that comments were bad. "Good code is self-documenting," became the rallying cry. "If you need a comment, your code isn't clean enough." Uncle Bob said it. Your tech lead repeated it. Conference talks reinforced it. And so an entire generation of developers stopped writing comments — and our codebases have been rotting ever since.
I'm going to argue that you should comment every single line of code. Not some lines. Not "tricky" lines. Every line. And I have the data to back it up.
The Self-Documenting Code Lie
Let's look at some supposedly "self-documenting" code:
JSfunction processOrder(order) {
if (!order.items.length) return null;
const total = order.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const tax = total * 0.0825;
return { ...order, total, tax, grandTotal: total + tax };
}
The self-documenting crowd says this is perfectly clear. And sure, if you're the person who wrote it, today, it's clear. But consider the questions this code doesn't answer:
- Why do we return
nullinstead of throwing an error for empty orders? - Is 0.0825 a Texas tax rate? A company-wide rate? A hardcoded bug?
- Should this function handle discounts? Is the absence of discount logic intentional or an oversight?
- Who calls this function? Is the return shape expected by a specific API contract?
- Was this function audited for financial compliance?
None of these answers exist in the code. They exist in someone's head, in a Slack thread from 2024, or nowhere at all. "Self-documenting" code documents the what. It never documents the why, the why not, or the what else was considered.
Here's the same function, properly commented:
JS// Calculates the final order total including tax.
// Returns null for empty orders because the checkout UI
// handles empty carts at the display layer (see CartView.jsx).
// Returning null here prevents a $0.00 charge from being
// created in Stripe, which caused incident INC-2847.
function processOrder(order) {
// Empty orders are valid (user cleared cart mid-checkout)
// but should not proceed to payment processing
if (!order.items.length) return null;
// Sum of (unit price * quantity) for each line item.
// Prices are stored in cents as integers to avoid
// floating-point rounding issues (see ADR-0023)
const total = order.items.reduce(
(sum, item) => sum + item.price * item.quantity, 0
);
// Texas state sales tax rate as of Jan 2025.
// This is hardcoded because we only operate in TX.
// TODO: When we expand to other states, move this
// to a tax service (tracked in JIRA-4521)
const tax = total * 0.0825;
// Return shape matches the OrderSummary type expected
// by the Stripe checkout session creation endpoint.
// Do not add fields without updating the Stripe integration.
return { ...order, total, tax, grandTotal: total + tax };
}
Same code. Ten times more useful. Every comment answers a question that the code cannot answer by itself, no matter how well it's named.
The Cost of Not Commenting
I analyzed three years of git history across four production codebases (totaling ~2.1 million lines of code). Specifically, I tracked how often developers modified code they didn't originally write, and what happened next.
| Codebase | Comment Ratio | Bug Rate After Modification by Non-Author |
|---|---|---|
| Project A (minimal comments) | 2% of lines | 34% of modifications introduced bugs |
| Project B (standard comments) | 8% of lines | 21% of modifications introduced bugs |
| Project C (heavy comments) | 22% of lines | 11% of modifications introduced bugs |
| Project D (every-line comments) | 45% of lines | 4% of modifications introduced bugs |
The correlation is striking. Project D, where the team commented nearly every line, had a bug introduction rate 8.5 times lower than Project A. This isn't because comments prevent bugs directly — it's because comments preserve intent, and understanding intent is the single most important factor in modifying code safely.
Every Line. Yes, Every Line.
I can hear you already: "But what about obvious lines? You don't need to comment i++!"
Actually, I think you do. Here's why.
What's obvious is subjective and changes over time. Today, i++ is obvious to you. But in
the context of a loop, the comment isn't about what i++ does mechanically — it's
about why this loop increments by one rather than some other value, or what i represents
in this context.
JS// Iterate through each patient record in today's appointment list.
// We process sequentially (not in parallel) because the EMR system
// has a rate limit of 1 request per 100ms (see their API docs, section 4.2)
for (let current_patient_index = 0; // Start from the first appointment of the day
current_patient_index < todays_appointments.length; // Process every scheduled appointment
current_patient_index++) { // Move to next appointment (sequential, not skipping)
// Retrieve the full patient record from the EMR system.
// This makes a network call and may take 100-500ms.
// If it fails, we log and continue (don't block other patients)
const patient_record = await fetchPatientRecord(
todays_appointments[current_patient_index].patient_id // EMR uses patient_id, not appointment_id
);
// ...processing continues
}
Is this verbose? Absolutely. Is every comment useful? Let me ask you this: if you needed to modify this code at 11 PM during an incident, would you rather have too many comments or too few?
Comments Are Not a Code Smell. Silence Is.
The Clean Code philosophy frames comments as a failure of expression. "If you need a comment, refactor until you don't." This advice is actively harmful because it conflates two different things:
- Comments that explain what code does — these can sometimes be eliminated through better naming
- Comments that explain why code exists, what alternatives were considered, and what constraints apply — these can never be expressed through naming alone
The industry threw out both categories because of the first one. It's like banning all medicine because some people take too much aspirin.
The Maintenance Argument, Debunked
"Comments go stale. Outdated comments are worse than no comments."
This is the most common objection, and it falls apart under scrutiny.
Code also goes stale. Variable names become inaccurate after refactors. Method names promise things the implementation no longer delivers. We don't respond to this by abolishing variable names — we respond by maintaining them. Comments deserve the same treatment.
If your team lets comments go stale, that's a code review problem, not a commenting problem. Add "verify comments still accurate" to your review checklist. Add a linter that flags modified lines with unmodified adjacent comments. Solve the process problem instead of abandoning the practice.
In our team's data, stale comments caused 3 bugs over three years. Missing comments — developers modifying code they didn't understand because there was no explanation — caused 147 bugs. The "stale comments" risk is real but fifty times smaller than the "no comments" risk.
What to Comment
If "every line" feels too extreme for your team to adopt immediately, here's a prioritized list. Start from the top and work your way down:
- Why this code exists — the business reason, the incident that prompted it, the requirement it fulfills
- Why it's written this way and not the obvious alternative — the performance constraint, the browser bug, the API limitation
- What will break if this code is modified — downstream dependencies, implicit contracts, timing assumptions
- What this code does, in domain language — translating implementation details into business concepts
- What each block of code accomplishes — section headers for logical groups of statements
- What each line does — yes, even the "obvious" ones, for completeness
Levels 1 through 3 are non-negotiable. If you're not writing those comments, you're not writing production-quality code — you're writing code that only works while you're still employed at this company.
Comments Are an Act of Empathy
When you write a comment, you are doing something remarkable: you are communicating with a person you will never meet, at a time you cannot predict, about a problem you've already solved. You are leaving a note for the next developer who will stare at your code at midnight, under pressure, with incomplete context, trying to fix something urgent.
Refusing to write that note because your code is "self-documenting" isn't engineering rigor. It's arrogance. It's the assumption that everyone who reads your code will have your context, your experience, and your understanding. They won't.
Write the comment. Write every comment. Your future colleagues will thank you.
This is an April 1st post. Please don't actually do any of this. No production apps were harmed in the making of this post. Probably.