Skip to content

Why Should Code Comments Explain 'Why' Not 'What'? A Developer's Guide to Better Documentation

Why Should Code Comments Explain “Why” Not “What”? A Developer’s Guide to Better Documentation

I spent three hours debugging a function last month. The comments said “parse date” and “check format” and “return result.” None of them explained why we used strptime instead of dateutil. None of them mentioned the production incident from six months ago. None of them helped me understand the code.

The comments were noise. They stated the obvious, duplicated what the code already showed, and added zero value.

Code comments should explain “why” rather than “what” because the “what” is already visible in the code itself. Effective comments clarify the constraints, trade-offs, and non-obvious decisions that led to the implementation.

The Problem: Comments That State the Obvious

I used to write comments like this:

user_processor.py (BAD)
# Loop through users
for user in users:
# Check if user is active
if user.is_active:
# Send email
send_email(user.email)

Every comment here is useless. The code already shows:

  • We’re looping through users
  • We’re checking if they’re active
  • We’re sending emails

These comments add maintenance burden without adding value. When I changed the logic later, I had to update both the code and the comments. Half the time, I forgot to update the comments, and they became misleading.

The Realization: Comments Should Answer What Code Cannot

A senior developer on my team reviewed my pull request and pointed out something that changed how I write comments:

“Your comments tell me what the code does. I can read the code for that. Tell me why it does it—the constraint, the trade-off, the thing that isn’t obvious.”

This hit home. The code answers “what.” Comments should answer “why.”

Here’s what I should have written:

date_parser.py (GOOD)
# Using strptime instead of dateutil because dateutil silently accepts
# ambiguous date formats (e.g., "01/02/03") which caused production
# incidents when users entered dates in unexpected formats.
# See incident #234 for details.
def parse_date(date_string: str) -> datetime:
return datetime.strptime(date_string, "%Y-%m-%d")

This comment explains:

  • Why we chose this approach (dateutil’s behavior)
  • What constraint influenced the decision (ambiguous formats)
  • What context a future reader needs (incident reference)

The code shows what happens. The comment shows why it matters.

Common Mistakes I Made

Mistake 1: Explaining Syntax

threshold_check.py
# BAD: Explains Python syntax
# Check if x is greater than 5
if x > 5:
process_order()
# GOOD: Explains business logic
# Threshold is 5 because orders below this amount have negative
# margin after shipping costs (per finance team analysis, Q3 2025)
if x > 5:
process_order()

The first comment explains Python. The second explains business.

Mistake 2: Restating Function Names

price_calculator.py
# BAD: Function name already says this
# Calculate total price
def calculate_total_price(items):
pass
# GOOD: Explains the calculation logic
# Uses pre-tax prices and applies regional tax rates from the
# tax_service API. Does not include shipping (calculated separately).
def calculate_total_price(items):
pass

Good function names make “what” comments redundant.

Mistake 3: Commenting Obvious Code

counter.py
# BAD: State the obvious
# Set counter to zero
counter = 0
# GOOD: Explain non-obvious initialization
# Counter starts at 1 instead of 0 because we're counting iterations,
# and the first iteration counts as "1st" in user-facing logs
counter = 1

If the comment just restates the code, delete it.

Mistake 4: Not Updating Comments

cache_handler.py
# BAD: Outdated comment
# Cache expires in 1 hour
# (Actually changed to 30 minutes 6 months ago)
cache.set(key, value, ttl=1800)
# GOOD: Keep current or remove
cache.set(key, value, ttl=1800) # 30-minute TTL per performance analysis

Outdated comments are worse than no comments.

Real-World Transformation

I refactored a JavaScript module that was full of “what” comments:

userService.js (BEFORE)
// Get user by ID
function getUser(id) {
// Fetch from database
const user = db.query('SELECT * FROM users WHERE id = ?', [id]);
// Return user
return user;
}
// Validate email
function validateEmail(email) {
// Check if email contains @
return email.includes('@');
}

All noise. I rewrote it with “why” comments:

userService.js (AFTER)
// Uses raw SQL instead of ORM because the ORM adds ~50ms overhead
// per query. This endpoint processes 10k+ requests/minute during
// peak hours, so the performance gain matters.
function getUser(id) {
const user = db.query('SELECT * FROM users WHERE id = ?', [id]);
return user;
}
// Simplified validation for MVP - only checks for @ symbol.
// TODO: Implement full RFC 5322 validation before v2.0 launch.
// See: https://github.com/team/project/issues/123
function validateEmail(email) {
return email.includes('@');
}

The second version actually helps future developers understand the constraints and trade-offs.

Why This Matters

Faster Onboarding

Before my team adopted this approach, new developers constantly asked “why does this work like this?” during code reviews. The answers lived in tribal knowledge—people’s heads, Slack threads, forgotten Jira tickets.

After we started writing “why” comments, the context was in the code. New team members understood decisions immediately.

Better Code Reviews

Reviewers used to ask clarifying questions about non-obvious code. Now the comments explain the reasoning upfront. Fewer back-and-forth comments, faster review cycles.

Reduced Maintenance Burden

“What” comments become outdated quickly because they duplicate the code. “Why” comments remain relevant longer because the reasoning behind a decision rarely changes even if the implementation does.

The Test I Use Now

Before adding a comment, I ask myself:

“Does this explain something the code cannot?”

If the answer is no, I delete the comment or rewrite it to explain the “why.”

Here’s my mental checklist:

  • Does this comment explain a constraint?
  • Does it explain a trade-off?
  • Does it reference context the code doesn’t show?
  • Does it prevent a future mistake?

If none of these apply, the comment is probably noise.

Practical Examples

Example 1: Performance Optimization

query_optimizer.py
# Using CTE instead of subquery because PostgreSQL's query planner
# creates a better execution plan with CTEs for datasets > 1M rows.
# Benchmarked: 2.3s → 0.4s with this change.
WITH active_users AS (
SELECT id FROM users WHERE last_login > NOW() - INTERVAL '30 days'
)
SELECT * FROM orders WHERE user_id IN (SELECT id FROM active_users);

The comment explains why we chose this approach and provides benchmark evidence.

Example 2: Security Decision

auth_middleware.js
// Token refresh happens 5 minutes before expiration, not at expiration,
// to avoid race conditions where the token expires mid-request.
// This caused intermittent 401 errors in production (see PR #892).
const REFRESH_BUFFER_MS = 5 * 60 * 1000;

The comment prevents someone from “optimizing” the buffer away and reintroducing the bug.

Example 3: Third-Party Integration

payment_gateway.py
# Stripe requires amount in cents, not dollars. This tripped us up
# during initial integration (see incident log from 2024-03-15).
amount_cents = int(amount_dollars * 100)

The comment warns about a non-obvious requirement that caused a past incident.

When to Skip Comments Entirely

Good code often needs no comments:

user_filter.py
# Self-documenting code - no comments needed
active_adult_users = [
user for user in users
if user.is_active and user.age >= 18
]

The variable name and code structure tell the story. Adding comments would be noise.

Conclusion

I learned that effective code comments explain the “why” behind decisions—constraints, trade-offs, and non-obvious context. They make code easier to maintain, faster to review, and simpler to onboard new developers.

Start today: before adding a comment, ask yourself “Does this explain something the code cannot?” If not, delete it or rewrite it.

The best comment is one that prevents a future developer from making the same mistake you almost made.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments