Introduction to Comments
Comments are an essential part of writing maintainable, understandable code. They are human-readable explanations embedded within source code that are ignored by compilers and interpreters. Good comments can make the difference between code that's a joy to work with and code that's a nightmare to maintain.
Key Concepts
- Documentation: Explain what code does and why
- Communication: Share intent with other developers
- Maintenance: Help future developers (including yourself)
- Debugging: Temporarily disable code during development
- Code Generation: Some comments can generate documentation
1. What Are Comments?
Comments are annotations in source code that are ignored by the compiler or interpreter. They serve as documentation for developers reading the code.
# This is a single-line comment
print("Hello, World!") # This is an inline comment
"""
This is a
multi-line comment
(or docstring)
"""
Why Comments Matter
# WITHOUT COMMENTS - Hard to understand def calc(x, y, z): a = x * y b = a + z c = b / 2 return c # WITH COMMENTS - Clear and maintainable def calculate_average_price(unit_price, quantity, tax): """ Calculate the average price per item including tax. Args: unit_price: Base price per unit quantity: Number of units purchased tax: Tax rate as decimal (e.g., 0.08 for 8%) Returns: Average price per unit after tax """ # Calculate subtotal subtotal = unit_price * quantity # Add tax to get total total_with_tax = subtotal + (subtotal * tax) # Return average price per unit return total_with_tax / quantity
2. Types of Comments
Single-Line Comments
# Python single-line comment x = 10 # Inline comment # Multiple single-line comments # This is a block of # single-line comments
// JavaScript single-line comment let x = 10; // Inline comment
// C single-line comment int x = 10; // Inline comment
-- SQL single-line comment SELECT * FROM users; -- This selects all users
Multi-Line Comments
""" Python multi-line comment This can span multiple lines Often used for docstrings """
/* JavaScript multi-line comment This can span multiple lines */
/* C multi-line comment This can span multiple lines */
/* * Java multi-line comment * Often formatted with asterisks */
<!-- HTML comment --> <!-- This is a multi-line HTML comment -->
Documentation Comments (Docstrings/DocComments)
# Python docstring
def greet(name):
"""
Greet a person by name.
Args:
name (str): The person's name
Returns:
str: A greeting message
Example:
>>> greet("Alice")
'Hello, Alice!'
"""
return f"Hello, {name}!"
/**
* JavaScript JSDoc comment
* @param {string} name - The person's name
* @returns {string} A greeting message
* @example
* greet("Alice"); // Returns "Hello, Alice!"
*/
function greet(name) {
return `Hello, ${name}!`;
}
/**
* Java Javadoc comment
* @param name The person's name
* @return A greeting message
*/
public String greet(String name) {
return "Hello, " + name + "!";
}
3. Comment Syntax by Language
Python
# Single-line comment """ Multi-line string (often used as docstring) Can be used for multi-line comments """ # Inline comment after code x = 5 # This is an inline comment # Type hints with comments def process(data: list) -> dict: # Process list into dictionary pass # TODO comments # TODO: Implement error handling # FIXME comments # FIXME: This function is too slow # NOTE comments # NOTE: This algorithm assumes sorted input
JavaScript/TypeScript
// Single-line comment
/* Multi-line
comment */
// Inline comment
let x = 5; // Set x to 5
// JSDoc documentation
/**
* Calculates the sum of two numbers
* @param {number} a - First number
* @param {number} b - Second number
* @returns {number} The sum
*/
function add(a, b) {
return a + b;
}
// TODO comments
// TODO: Add validation
// FIXME comments
// FIXME: Edge case not handled
Java
// Single-line comment
/* Multi-line
comment */
/**
* Javadoc comment
* @param name The name to greet
* @return Greeting message
*/
public String greet(String name) {
return "Hello, " + name + "!";
}
// TODO comments
// TODO: Implement this method
// FIXME comments
// FIXME: This has a bug
C/C++
// Single-line comment (C99 and later)
/* Multi-line
comment */
/**
* Doxygen comment
* @param x First number
* @param y Second number
* @return Sum of x and y
*/
int add(int x, int y) {
return x + y;
}
// Preprocessor comments
#define MAX_SIZE 100 // Maximum array size
// TODO comments
// TODO: Add error checking
Rust
// Single-line comment
/* Multi-line
comment */
/// Documentation comment (for following item)
/// This function adds two numbers
fn add(a: i32, b: i32) -> i32 {
a + b
}
//! Inner documentation comment (for containing item)
//! This module handles mathematical operations
// TODO comments
// TODO: Implement error handling
// FIXME comments
// FIXME: This function panics on overflow
Go
// Single-line comment (preferred in Go)
/* Multi-line
comment */
// Package comment
// Package math provides mathematical utilities
package math
// Add returns the sum of two integers
func Add(a, b int) int {
return a + b
}
// TODO comments
// TODO: Add more mathematical functions
Ruby
# Single-line comment
=begin
Multi-line comment
This is another way
=end
# Documentation comment
# This method greets the user
def greet(name)
"Hello, #{name}!"
end
# TODO comments
# TODO: Add validation
SQL
-- Single-line comment /* Multi-line comment */ -- Table documentation -- Stores user information CREATE TABLE users ( id INT PRIMARY KEY, -- User identifier name VARCHAR(100) -- User's full name ); -- TODO: Add indexes for better performance
HTML/CSS
<!-- HTML comment --> <div class="container"> <!-- Main content section --> <h1>Title</h1> </div>
/* CSS comment */
.container {
width: 100%; /* Full width container */
/* background: red; Disabled style */
}
4. Comment Best Practices
DO: Explain Why, Not What
# BAD - States the obvious # Increment counter by 1 counter += 1 # GOOD - Explains why # Move to next item after processing counter += 1 # BAD - Describes what the code does # Loop through all users for user in users: # Check if user is active if user.is_active: # Send email send_email(user) # GOOD - Explains business logic # Notify all active users about the new feature for user in users: if user.is_active: # Inactive users opted out of notifications send_feature_notification(user)
DO: Document Complex Logic
def calculate_discount(price, customer_type, loyalty_years): """ Calculate discount based on customer type and loyalty. The discount logic follows company policy: - Regular customers: 5% discount - Premium customers: 10% discount + 1% per loyalty year - VIP customers: 15% discount + 2% per loyalty year, max 25% """ if customer_type == "regular": discount = 0.05 elif customer_type == "premium": discount = 0.10 + (0.01 * min(loyalty_years, 5)) else: # VIP discount = min(0.15 + (0.02 * loyalty_years), 0.25) return price * (1 - discount)
DO: Use TODO and FIXME Comments
def process_data(data): # TODO: Add validation for empty data # TODO: Handle edge cases # FIXME: This algorithm is O(n²), optimize for large datasets result = [] for i in range(len(data)): for j in range(len(data)): # NOTE: This comparison might be slow if data[i] == data[j]: result.append(data[i]) return result
DO: Document Public APIs
class UserService:
"""
Service for managing user accounts.
This class handles user registration, authentication,
and profile management.
"""
def register_user(self, email, password):
"""
Register a new user account.
Args:
email (str): User's email address
password (str): User's password (will be hashed)
Returns:
User: The newly created user object
Raises:
ValueError: If email already exists or is invalid
Example:
>>> service = UserService()
>>> user = service.register_user("[email protected]", "secure123")
>>> print(user.email)
[email protected]
"""
pass
DON'T: Comment Obvious Code
# BAD - Obvious comments
# Set x to 10
x = 10
# Check if x is greater than 5
if x > 5:
# Print message
print("x is greater than 5")
# GOOD - No comment needed for obvious code
x = 10
if x > 5:
print("x is greater than 5")
DON'T: Leave Dead Code Commented Out
# BAD - Commented out code that should be removed # def old_function(): # # Old implementation # pass # def another_old_function(): # # Another old function # pass # GOOD - Use version control instead def new_function(): # Clean, current implementation pass
DON'T: Write Misleading Comments
# BAD - Misleading comment # This function adds two numbers def multiply(a, b): return a * b # GOOD - Accurate comment # This function multiplies two numbers def multiply(a, b): return a * b
5. Documentation Comments
Python Docstrings
def complex_function(param1, param2, param3=None):
"""
Perform a complex operation with detailed documentation.
This function demonstrates proper docstring formatting.
Args:
param1 (int): First parameter description
param2 (str): Second parameter description
param3 (list, optional): Third parameter description. Defaults to None.
Returns:
bool: True if successful, False otherwise
Raises:
ValueError: If param1 is negative
TypeError: If param2 is not a string
Examples:
>>> complex_function(5, "test")
True
>>> complex_function(-1, "test")
Traceback (most recent call last):
...
ValueError: param1 cannot be negative
Notes:
This function uses O(n log n) algorithm.
For large inputs, consider using optimized version.
"""
if param1 < 0:
raise ValueError("param1 cannot be negative")
if not isinstance(param2, str):
raise TypeError("param2 must be a string")
# Implementation here
return True
Class Documentation
class DataProcessor:
"""
A class for processing and transforming data.
This class provides methods for cleaning, transforming,
and validating data before analysis.
Attributes:
name (str): The name of the processor
config (dict): Configuration settings
Example:
>>> processor = DataProcessor("cleaner", {"verbose": True})
>>> processor.process(raw_data)
[1, 2, 3, 4, 5]
"""
def __init__(self, name, config=None):
"""
Initialize the DataProcessor.
Args:
name (str): Name of the processor
config (dict, optional): Configuration settings
"""
self.name = name
self.config = config or {}
def process(self, data):
"""
Process the input data.
Args:
data (list): Raw data to process
Returns:
list: Processed data
"""
pass
Module Documentation
""" Data Processing Module ====================== This module provides utilities for processing and cleaning data. Modules exported by this package: - data_processor: Core data processing functionality - validators: Data validation utilities - transformers: Data transformation functions Examples: >>> from data_processor import DataProcessor >>> processor = DataProcessor() >>> result = processor.process([1, 2, 3, 4, 5]) >>> print(result) [2, 4, 6, 8, 10] """
6. Special Comment Patterns
TODO Comments
# TODO: Implement error handling # TODO(John): Add validation for edge cases # TODO(bug #123): Fix performance issue def process_file(filename): # TODO: Add file existence check # TODO: Handle different file formats # TODO: Add progress tracking for large files pass
FIXME Comments
# FIXME: This function fails with empty input # FIXME: Memory leak in error path # FIXME: Race condition in concurrent access def divide(a, b): # FIXME: Division by zero not handled return a / b
NOTE Comments
# NOTE: This algorithm assumes sorted input # NOTE: This function is not thread-safe # NOTE: This code is optimized for speed, not readability def binary_search(arr, target): # NOTE: arr must be sorted in ascending order left, right = 0, len(arr) - 1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: left = mid + 1 else: right = mid - 1 return -1
HACK Comments
# HACK: This is a temporary workaround
# HACK: Using this because the API doesn't support direct access
# HACK: This should be replaced with proper solution
def get_data():
# HACK: Sleep to avoid rate limiting (should use proper backoff)
time.sleep(1)
# HACK: Using private API (should use public endpoint)
response = requests.get("https://api.example.com/internal/data")
return response.json()
Deprecation Comments
# DEPRECATED: Use new_function() instead def old_function(): """Deprecated. Use new_function() instead.""" pass # @deprecated(version="2.0", reason="Use new_function instead") def old_method(): pass
Security Comments
# SECURITY: This endpoint requires authentication # SECURITY: SQL injection risk - use parameterized queries # SECURITY: Sanitize user input before using def get_user_data(user_id): # SECURITY: Validate user_id is integer # SECURITY: Check user permissions before returning data pass
7. Comment Generation Tools
Generating Documentation from Comments
# Python - Generate documentation from docstrings # pip install sphinx sphinx-quickstart sphinx-build -b html source/ build/ # JavaScript - Generate JSDoc documentation # npm install -g jsdoc jsdoc yourcode.js # Java - Generate Javadoc javadoc -d docs/ *.java # Rust - Generate documentation cargo doc # Go - Generate documentation godoc -http=:6060
Example: JSDoc in JavaScript
/**
* User management module
* @module user
*/
/**
* Create a new user
* @param {string} name - User's full name
* @param {number} age - User's age
* @param {string} email - User's email address
* @returns {Object} User object
* @throws {Error} If email is invalid
*/
function createUser(name, age, email) {
// Implementation
}
/**
* @typedef {Object} User
* @property {string} name - User's name
* @property {number} age - User's age
* @property {string} email - User's email
*/
/**
* @class
* @classdesc Represents a user in the system
*/
class User {
/**
* Create a user
* @param {string} name - User's name
* @param {number} age - User's age
*/
constructor(name, age) {
this.name = name;
this.age = age;
}
/**
* Get user's greeting
* @returns {string} Greeting message
*/
greet() {
return `Hello, I'm ${this.name}`;
}
}
Example: Python Sphinx
""" Module for mathematical operations. This module provides basic mathematical functions. """ def add(a, b): """ Add two numbers. :param a: First number :type a: int or float :param b: Second number :type b: int or float :return: Sum of a and b :rtype: int or float Example: >>> add(2, 3) 5 >>> add(2.5, 3.5) 6.0 """ return a + b class Calculator: """ A simple calculator class. :ivar memory: Current value in memory :type memory: float """ def __init__(self): """Initialize calculator with memory set to 0.""" self.memory = 0 def add_to_memory(self, value): """ Add value to memory. :param value: Value to add :type value: float """ self.memory += value
8. Comment Anti-Patterns
1. Redundant Comments
# BAD - Redundant comments # Increment i i += 1 # Check if user exists if user_exists(user_id): # Send welcome email send_welcome_email(user) # GOOD - Self-documenting code i += 1 # The code is clear enough if user_exists(user_id): send_welcome_email(user)
2. Outdated Comments
# BAD - Outdated comment # This function calculates the total price including tax # Tax rate is 8% def calculate_total(price, quantity): # Now uses variable tax rate, but comment is outdated return price * quantity * tax_rate # GOOD - Update comments with code changes def calculate_total(price, quantity, tax_rate): """Calculate total price including tax.""" return price * quantity * tax_rate
3. Overly Verbose Comments
# BAD - Too much detail # This function starts by checking if the input is valid. # Then it creates an empty list to store results. # Then it loops through each item in the input. # For each item, it processes it and adds to the list. # Finally, it returns the list. def process_items(items): result = [] for item in items: processed = process_item(item) result.append(processed) return result # GOOD - Let the code speak for itself def process_items(items): """Process each item and return results.""" return [process_item(item) for item in items]
4. Comments Instead of Better Code
# BAD - Comment explains confusing code # If x is greater than y and not equal to z, or if flag is true... if (x > y and x != z) or flag: do_something() # GOOD - Refactor for clarity is_valid_condition = (x > y and x != z) or flag if is_valid_condition: do_something() # Or use well-named variables has_priority = (x > y and x != z) or flag if has_priority: do_something()
5. Commented-Out Code
# BAD - Dead code cluttering the file def process_data(data): # result = [] # for item in data: # result.append(item * 2) # return result # New implementation return [item * 2 for item in data] # GOOD - Remove dead code, use version control def process_data(data): """Double each value in the data list.""" return [item * 2 for item in data]
9. Best Practices Summary
DO's ✅
# DO explain why, not what # Process in reverse order to avoid index shifting for i in range(len(arr)-1, -1, -1): process(arr[i]) # DO document complex algorithms # Use binary search for O(log n) lookup # Since array is sorted, we can use binary search def find_element(arr, target): pass # DO use TODO for planned work # TODO: Add caching to improve performance def fetch_data(): pass # DO document public APIs def public_function(param): """ Brief description. Args: param: Description Returns: Description """ pass # DO update comments when code changes def calculate_discount(price): # Discount is now 10% for all customers (changed from 5%) return price * 0.9
DON'Ts ❌
# DON'T state the obvious x = 5 # Set x to 5 # DON'T comment out code # def old_function(): # pass # DON'T leave outdated comments # This function handles v1 API def handle_api_v2(): pass # DON'T write misleading comments # Add two numbers def multiply(a, b): return a * b # DON'T use comments instead of good naming # Check if user is admin if user.role == "admin": grant_access()
10. Code Examples
Example 1: Well-Commented Function
def calculate_compound_interest(principal, rate, time, compounds_per_year=12): """ Calculate compound interest over time. Compound interest formula: A = P(1 + r/n)^(nt) Where: A = Final amount P = Principal (initial investment) r = Annual interest rate (as decimal) n = Number of times interest compounds per year t = Time in years Args: principal (float): Initial investment amount rate (float): Annual interest rate (e.g., 0.05 for 5%) time (float): Investment period in years compounds_per_year (int): Compounding frequency (default: monthly) Returns: float: Final amount after interest Example: >>> calculate_compound_interest(1000, 0.05, 10) 1647.01 # $1,000 at 5% for 10 years """ # Calculate using compound interest formula # Convert rate to decimal if provided as percentage if rate > 1: rate = rate / 100 # Calculate final amount amount = principal * (1 + rate / compounds_per_year) ** (compounds_per_year * time) # Return rounded to 2 decimal places (currency format) return round(amount, 2)
Example 2: Well-Commented Class
class DatabaseConnection:
"""
Manages database connections with connection pooling.
This class handles creating, maintaining, and reusing
database connections to improve performance.
Attributes:
host (str): Database server hostname
port (int): Database server port
pool_size (int): Maximum number of connections in pool
connections (list): Active connection pool
Example:
>>> db = DatabaseConnection("localhost", 5432, pool_size=5)
>>> with db.get_connection() as conn:
... result = conn.execute("SELECT * FROM users")
"""
def __init__(self, host, port, pool_size=10):
"""
Initialize database connection manager.
Args:
host (str): Database server hostname
port (int): Database server port
pool_size (int, optional): Max connections. Defaults to 10.
Raises:
ValueError: If pool_size is less than 1
"""
if pool_size < 1:
raise ValueError("Pool size must be at least 1")
self.host = host
self.port = port
self.pool_size = pool_size
self.connections = [] # Pool of available connections
self.active = [] # Currently active connections
def get_connection(self):
"""
Get a database connection from the pool.
Returns:
Connection: Database connection object
Note:
Connection should be returned with release_connection()
when done to avoid leaks.
"""
# Try to get existing connection
if self.connections:
conn = self.connections.pop()
self.active.append(conn)
return conn
# Create new connection if pool not full
if len(self.active) < self.pool_size:
conn = self._create_connection()
self.active.append(conn)
return conn
# Pool exhausted, wait for connection
# TODO: Implement waiting mechanism
raise RuntimeError("No available connections")
def release_connection(self, conn):
"""
Return connection to pool.
Args:
conn: Connection to release
Note:
Should always be called after get_connection()
"""
if conn in self.active:
self.active.remove(conn)
self.connections.append(conn)
def _create_connection(self):
"""
Create a new database connection.
Returns:
Connection: New database connection
Note:
Private method, not for external use
"""
# Implementation would create actual database connection
pass
Example 3: Module Documentation
""" Data Processing Module ====================== This module provides utilities for processing and analyzing data. Modules exported by this package: - `cleaners`: Data cleaning functions - `transformers`: Data transformation utilities - `validators`: Data validation tools Example usage: >>> from data_processor import cleaners, transformers >>> data = [1, 2, None, 4, 5] >>> cleaned = cleaners.remove_nulls(data) >>> scaled = transformers.min_max_scale(cleaned) >>> print(scaled) [0.0, 0.25, 0.75, 1.0] Version: 2.1.0 Author: John Doe License: MIT """ from .cleaners import remove_nulls, fill_missing from .transformers import scale, normalize from .validators import validate_range, validate_type __version__ = "2.1.0" __all__ = ["remove_nulls", "fill_missing", "scale", "normalize"]
11. Tools and Practices
Code Review Checklist for Comments
## Code Review: Comment Checklist ### Documentation Comments - [ ] Public functions have docstrings - [ ] Classes have class documentation - [ ] Modules have module-level documentation - [ ] Complex algorithms have explanatory comments - [ ] Magic numbers are explained ### Comment Quality - [ ] Comments explain "why", not "what" - [ ] No redundant or obvious comments - [ ] Comments are up-to-date with code - [ ] No commented-out code - [ ] TODO/FIXME comments are tracked ### Consistency - [ ] Consistent comment style throughout - [ ] Proper formatting for docstrings - [ ] Consistent use of comment tags (TODO, FIXME, NOTE)
Comment Linters
# Python - flake8 checks comment formatting pip install flake8 flake8 --ignore=E501 code.py # JavaScript - ESLint with JSDoc plugin npm install -g eslint eslint-plugin-jsdoc # General - ShellCheck for shell scripts shellcheck script.sh # Markdown - Markdown lint for documentation npm install -g markdownlint-cli
Pre-commit Hooks for Comments
# .pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: check-added-large-files - id: check-merge-conflict - id: check-yaml - repo: https://github.com/psf/black rev: 22.10.0 hooks: - id: black - repo: https://github.com/PyCQA/flake8 rev: 5.0.4 hooks: - id: flake8 args: [--max-line-length=88, --ignore=E501]
12. Real-World Examples
Example: E-commerce Checkout Function
def process_checkout(cart_items, user, payment_info, shipping_address):
"""
Process e-commerce checkout.
This function handles the complete checkout process including:
1. Cart validation
2. Inventory check
3. Price calculation
4. Payment processing
5. Order creation
Args:
cart_items (list): List of CartItem objects
user (User): Current user
payment_info (PaymentInfo): Payment details
shipping_address (Address): Shipping destination
Returns:
Order: Created order object
Raises:
InsufficientInventoryError: If items out of stock
PaymentError: If payment fails
ValidationError: If cart is empty or invalid
Note:
This function is transactional - if any step fails,
all changes are rolled back.
Example:
>>> items = [CartItem(product_id=1, quantity=2)]
>>> user = User(id=123)
>>> payment = PaymentInfo(token="tok_visa")
>>> address = Address(street="123 Main St", city="Boston")
>>> order = process_checkout(items, user, payment, address)
>>> print(order.status)
'confirmed'
"""
# Step 1: Validate cart
if not cart_items:
raise ValidationError("Cart is empty")
# Step 2: Check inventory
# For each item, verify stock availability
for item in cart_items:
# TODO: Add caching for frequently checked items
if not check_inventory(item.product_id, item.quantity):
raise InsufficientInventoryError(f"Item {item.product_id} out of stock")
# Step 3: Calculate total with tax and shipping
subtotal = sum(item.price * item.quantity for item in cart_items)
tax = calculate_tax(subtotal, shipping_address.state)
shipping_cost = calculate_shipping(cart_items, shipping_address)
total = subtotal + tax + shipping_cost
# Step 4: Process payment
# FIXME: Add retry logic for payment failures
payment_result = process_payment(payment_info, total)
if not payment_result.success:
raise PaymentError(payment_result.error_message)
# Step 5: Create order record
order = Order.create(
user_id=user.id,
items=cart_items,
total=total,
payment_id=payment_result.id,
shipping_address=shipping_address
)
# Step 6: Update inventory (atomic)
for item in cart_items:
reduce_inventory(item.product_id, item.quantity)
# Step 7: Send confirmation
# NOTE: Use async queue for email to avoid blocking
send_order_confirmation.delay(user.email, order.id)
return order
Conclusion
Comments are a vital part of writing maintainable, understandable code:
Key Takeaways
- Purpose: Comments explain "why", not "what"
- Documentation: Use docstrings for public APIs
- Clarity: Good comments make code self-documenting
- Maintenance: Update comments when code changes
- Tags: Use TODO, FIXME, NOTE for tracking
- Consistency: Follow language-specific comment conventions
Best Practices Summary
| Practice | Do | Don't |
|---|---|---|
| Explain | Why code exists | What code does |
| Document | Complex algorithms | Obvious operations |
| Update | Comments with code | Leave outdated comments |
| Use | Docstrings for APIs | Over-comment |
| Clean | Remove dead code | Leave commented code |
| Tag | TODOs and FIXMEs | Ignore planned work |
Final Thoughts
Good comments are like good documentation - they help others (and your future self) understand your code. They're an investment in code maintainability that pays dividends throughout the software lifecycle. Write comments as if the person reading them will be you in six months, because it often will be!
Remember: The best comment is often no comment at all - code that's so clear it explains itself. When you do need comments, make them count by explaining the reasoning behind decisions, not just what the code is doing.