Why commenting matters for code quality and maintainability
When to comment and when not to comment
Practical strategies for writing clear, helpful, and meaningful comments
🎯 Goal
Learning to write comments that clarify intent, not echo code.
Despite Python examples, all the mentioned strategies are language-agnostic. None of this advice is dogma; there can be valid reasons and conventions to break these rules.
“The purpose of commenting is to help the reader know as much as the writer did.”
- Boswell & Foucher
Why Comments Matter
Good comments act as guideposts for navigating complex code.
Why It’s Challenging
Multiple pressures during development make thoughtful commenting difficult.
flowchart TD
A[Deadline Pressure] --> D[High Cognitive Load]
B[Complex Problem Solving] --> D
C[Implementation Focus] --> D
D --> E[Commenting Shortcuts]
E --> F[No Comments]
E --> G[Redundant Comments]
E --> H[Obsolete Comments]
style A fill:#E8EAF6,color:#000
style B fill:#E8EAF6,color:#000
style C fill:#E8EAF6,color:#000
style D fill:#FFE082,color:#000
style E fill:#FFCC80,color:#000
style F fill:#FFAB91,color:#000
style G fill:#FFAB91,color:#000
style H fill:#FFAB91,color:#000
N.B. Documentation vs. Comments
def is_valid_email(email: str) -> bool: “““Check if email address is valid.
# Password must have: uppercase letter, digit,# special char (@$!%*?&), min 8 characters# Note: ASCII-only, no entropy checkpattern =r'^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'if re.match(pattern, password):returnTrue
❌ Unexplained bit manipulation
def is_power_of_two(n):return n >0and (n & (n -1)) ==0
✅ Logic clarified
def is_power_of_two(n):# Powers of 2 have single bit set;# n & (n-1) clears lowest bit, yields 0return n >0and (n & (n -1)) ==0
Explain Unidiomatic Code
When you intentionally deviate from language conventions, explain why the idiomatic approach doesn’t work and preempt questions.
❌ No explanation for unusual approach
# Process itemsresult = []for item in items:if item >0: result.append(item *2)
✅ Explains deviation from idiom
# Use explicit loop to allow early break on error# (list comprehension evaluates all items upfront)result = []for item in items:if item >0: result.append(item *2)
# Cannot use context manager: file handle must remain# open for async callback (library limitation)file=open('data.txt', 'r')content =file.read()file.close()
Highlight Known Flaws
It’s acceptable to document known issues, limitations, or future improvements using action comments.
❌ Vague action comment
# TODO: fix thisdef process_data(items):return [x *2for x in items]
✅ Specific with tracking
# TODO: Add validation for empty list (issue #847)def process_data(items):return [x *2for x in items]
Common Action Comment Tags
Tag
Purpose
Example
TODO
Planned improvement or missing feature
# TODO: Add caching (issue #123)
FIXME
Known bug that needs fixing
# FIXME: Fails on negative input (#456)
HACK
Temporary workaround for a problem
# HACK: API bug workaround (ticket #789)
NOTE
Important clarification or caveat
# NOTE: Must run before init()
OPTIMIZE
Performance improvement opportunity
# OPTIMIZE: Use binary search (#234)
Tip: Use the Better Comments VS Code extension to highlight these tags in your editor.
Aid Comprehension with Examples
Well-chosen examples often clarify complex code more effectively than detailed comments alone.
❌ Comment without example
# Modulo with negatives wraps backward, not toward zeroindex = position % array_length
✅ Comment with examples
# Modulo with negatives wraps backward, not toward zero# -5 % 3 = 1 (not -2), 5 % 3 = 2# Useful for circular array indexingindex = position % array_length
When implementing specs, algorithms, or standards, provide references where readers can learn more.
# ISO 8601 date parsing# https://en.wikipedia.org/wiki/ISO_8601def parse_iso_date(date_string): pattern =r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$'return datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%SZ')
Use Comments as Cautionary Text
Cautionary comments prevent well-intentioned “fixes” that break subtle constraints by warning against seemingly obvious “improvements”.
❌ No warning about dead-end
# Linear search through usersfor user in users:if user.id== target_id:return user
✅ Warns against futile optimization
# Linear search through users# DON'T optimize: list is always <20 items# Binary search tested in PR #456: 0.01ms vs 0.009msfor user in users:if user.id== target_id:return user
❌ Missing context for constraint
sleep(0.1)response = api.fetch(url)
✅ Explains critical timing
# WARNING: 100ms delay required by API rate limit# Removing causes 429 errors (see incident #2341)sleep(0.1)response = api.fetch(url)
Explain Security-Sensitive Code
Security comments prevent dangerous “simplifications” that introduce vulnerabilities by explaining why “obvious” optimizations break security boundaries.
# SECURITY: HTML escape before display# Only '<' escaped; XSS still possible via other chars# TODO: Use proper HTML sanitization libraryoutput = user_input.replace('<', '<')display(output)
Document Concurrency Invariants
Concurrency comments prevent race conditions from “optimizations” that remove checks by explaining locking order, invariants, and why seemingly redundant logic matters.
# Someone removed "unnecessary" lock to reduce nestingifself.state =='ready':self.state ='processing'returnself.process()
✅ Explains invariant and lock order
# INVARIANT: state transitions require lock# Lock order: self.lock before db.lock (deadlock)# Double-check pattern: state may change during waitwithself.lock:ifself.state =='ready':self.state ='processing'returnself.process()
Organize Long Functions
When a function is legitimately complex and shouldn’t be broken down further, use comments to delineate logical sections.
Note: Refactoring into smaller functions is still preferable unless there’s a strong reason (tight I/O coupling, measured performance need, transaction boundaries).
Use section comments to:
Group related operations
Mark distinct processing phases
Improve readability of long functions
Guide readers through complex logic
def process_order(order):# Validate order data ...# Calculate pricing and discounts ...# Check inventory availability ...# Process payment ...# Update database and send confirmations ...return result
How to Comment
Keep them precise and compact.
Use precise and actionable wording
Use information-dense words
❌ Verbose
# Store function results based on inputs# to avoid recomputing same values@save_resultsdef calculate(x, y):return x ** y
✅ Uses technical term
# Memoize expensive computation@save_resultsdef calculate(x, y):return x ** y
Avoid pronouns
❌ Ambiguous pronouns
# Sync local cache with remote database# and invalidate itsync(local_cache, remote_db)invalidate()
✅ Specific nouns
# Sync local cache with remote database# and invalidate local cachesync(local_cache, remote_db)invalidate()
Explain intent, not implementation
❌ Lower-level details
# Loop through array and add to sumtotal =sum(prices)
✅ Higher-level intent
# Calculate order total for tax computationtotal =sum(prices)
Keep comments current and specific
Keep comments updated
❌ Comment lies after refactor
# Calculate average of all valuesreturn median(values) # Changed from mean()!
✅ Comment matches code
# Calculate median for robust aggregation# (changed from mean to handle outliers)return median(values)
Use specific references
❌ Vague reference
# See the function aboveapply_discount()
✅ Clear reference
# Rate logic in pricing.calculate_discount()apply_discount()
timeout =30# API rate limitretries =3# Network failuresbuffer_size =1024# Chunk size in bytes
Block comments with breathing room
## This is a complex algorithm requiring# detailed explanation across multiple lines#def complex_algorithm(): ...
Consistent capitalization
# Calculate total pricetotal =sum(prices)# Apply discount to premium usersif user.is_premium: apply_discount()
Consistent punctuation
# Option 1: No punctuation# Calculate average# Filter outliers# Return result# Option 2: Full sentences# Calculate the average of all values.# Filter out statistical outliers.# Return the final result.
Tip: Configure your linter/formatter to enforce these aesthetic choices consistently.
Tools & Techniques
“Code never lies, comments sometimes do.”
— Ron Jeffries
Tool Limitations
What tools CAN do:
Check comment formatting and style
Flag TODO/FIXME comments
Detect missing documentation
Generate basic API documentation
What they CANNOT do:
Understand if comments explain the “why”
Assess comment usefulness and clarity
Determine if business context is missing
Evaluate comment accuracy after code changes
Judge whether comments add value
The fundamental limitation
Tools can enforce format but not value. Good commenting requires human judgment about what information is helpful.
AI as an Ally
Why AI tools can help:
Analyze code complexity and suggest documentation
Identify business logic that needs explanation
Check comment clarity and helpfulness
flowchart TD
H[Human] --> S[Quality Comments]
A[AI] --> S[Quality Comments]
H -.->|Collaborates| A
A -.->|Feedback| H
style H fill:#BBDEFB,color:#000
style A fill:#BBDEFB,color:#000
style S fill:#A5D6A7,color:#000
LLM-Generated Comments
LLMs can overcomment or generate redundant comments, but they can also tighten verbose ones.
Human-generated comment:- # Retry up to 3 times with exponential backoff- # to handle transient network failuresLLM-suggested improvement:+ # Retry 3x with exponential backoff for network errors
Always review LLM-generated comments critically.
Code Review
flowchart TD
A[Code Review] --> B[Lower Cognitive Load]
B --> C[Fresh Perspective]
B --> D[Focus on Clarity]
C --> E[Question Assumptions]
C --> F[Spot Missing Context]
C --> O[Find Obsolete Comments]
D --> G[Evaluate Intent]
D --> H[Assess Usefulness]
E --> I[Better Comments]
F --> I
G --> I
H --> I
O --> I
style A fill:#90CAF9,color:#000
style B fill:#CE93D8,color:#000
style C fill:#E1F5FE,color:#000
style D fill:#E1F5FE,color:#000
style E fill:#FFE082,color:#000
style F fill:#FFE082,color:#000
style G fill:#FFE082,color:#000
style H fill:#FFE082,color:#000
style O fill:#FFE082,color:#000
style I fill:#B39DDB,color:#000
Fresh eyes catch what you miss: outdated comments, missing context, and unclear intent!
Summary
“Good code is its own best documentation.”
— Steve McConnell
Comment decision workflow
Comment Decision Workflow
When encountering unclear code, prioritize refactoring over commenting. Only add comments for inherently non-obvious logic.
flowchart LR
A[Code<br/>unclear?] --> B{Can<br/>rename/refactor?}
B -->|Yes| C[Rename/<br/>Refactor]
C --> D{Still<br/>non-obvious?}
B -->|No| D
D -->|Yes| E[Comment<br/>WHY]
D -->|No| F[No comment<br/>needed]
style A fill:#FFE082,color:#000
style B fill:#90CAF9,color:#000
style C fill:#B39DDB,color:#000
style D fill:#90CAF9,color:#000
style E fill:#FFF59D,color:#000
style F fill:#B39DDB,color:#000
“Don’t comment bad code — rewrite it.”
— Kernighan & Plaugher
Workflow in Action
Applying the decision workflow: refactor first, comment only what remains non-obvious.
❌ Poor naming + excessive comments
def calc(d, t):# Initialize result variable r =0# Loop through data arrayfor i inrange(len(d)):# Check if threshold is exceededif d[i] >= t:# Add to running total r = r + d[i]# Otherwise skip the valueelse:continue# Return the final resultreturn r
✅ Clear naming + selective comment
def sum_values_above_threshold(data, threshold):# Use >= instead of > due to sensor calibration specs:# boundary readings confirmed valid (see ticket #891) filtered_values = (value for value in data if value >= threshold)returnsum(filtered_values)
Comment decision workflow
Comment Decision Workflow
When encountering unclear code, prioritize refactoring over commenting. Only add comments for inherently non-obvious logic.
flowchart LR A[Code<br/>unclear?] --> B{Can<br/>rename/refactor?} B -->|Yes| C[Rename/<br/>Refactor] C --> D{Still<br/>non-obvious?} B -->|No| D D -->|Yes| E[Comment<br/>WHY] D -->|No| F[No comment<br/>needed] style A fill:#FFE082,color:#000 style B fill:#90CAF9,color:#000 style C fill:#B39DDB,color:#000 style D fill:#90CAF9,color:#000 style E fill:#FFF59D,color:#000 style F fill:#B39DDB,color:#000“Don’t comment bad code — rewrite it.”
— Kernighan & Plaugher