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:
# Loop through usersfor 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:
# 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
# BAD: Explains Python syntax# Check if x is greater than 5if 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
# BAD: Function name already says this# Calculate total pricedef 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): passGood function names make “what” comments redundant.
Mistake 3: Commenting Obvious Code
# BAD: State the obvious# Set counter to zerocounter = 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 logscounter = 1If the comment just restates the code, delete it.
Mistake 4: Not Updating Comments
# 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 removecache.set(key, value, ttl=1800) # 30-minute TTL per performance analysisOutdated comments are worse than no comments.
Real-World Transformation
I refactored a JavaScript module that was full of “what” comments:
// Get user by IDfunction getUser(id) { // Fetch from database const user = db.query('SELECT * FROM users WHERE id = ?', [id]);
// Return user return user;}
// Validate emailfunction validateEmail(email) { // Check if email contains @ return email.includes('@');}All noise. I rewrote it with “why” comments:
// 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/123function 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
# 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
// 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
# 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:
# Self-documenting code - no comments neededactive_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:
- 👨💻 Reddit Discussion on Code Comments
- 👨💻 Clean Code by Robert C. Martin
- 👨💻 The Art of Readable Code
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments