Good code comments explain why, not what. Code itself shows what it does. Comments explain reasoning, context, and decisions that code cannot express. According to research from Google's code review guidelines, well-commented code reduces maintenance time by 40% because developers understand intent without archaeology. Writing effective comments is skill that distinguishes professional developers from amateurs.
What Makes a Good Code Comment?
Good comments explain non-obvious decisions. Why did you choose this algorithm over alternatives? Why does this hack exist? What constraint required this workaround? These explanations save future developers (including you) from repeating past investigations. Comment: "Using bubble sort here because dataset is always under 10 items and simplicity matters more than efficiency." This explains choice other developers might question.
Comments should focus on why and when, not what and how. Code shows what and how. Bad comment: "Increments counter." above `counter++`. Good comment: "Track retry attempts to enforce 3-try maximum before giving up." Good comment explains why counter exists and what business rule it enforces. Developer reading code understands not just mechanics but purpose.
Effective comments assume reader understands programming language basics but lacks context about your specific problem domain. You need not explain what loops do. You should explain why this particular loop structure solves your particular problem. Balance between obvious and obscure. When uncertain whether to comment, err toward commenting. Unnecessary comment is better than missing necessary one.
When Should You Write Comments?
Comment complex algorithms or non-obvious logic. If code took you 30 minutes to figure out, it will take others 30 minutes too. Save them time with explanation. Regular expressions always need comments explaining what they match. Bit manipulation needs comments explaining the math. Any code where you had "aha!" moment after struggle needs comment preventing others from same struggle.
Explain workarounds for bugs in libraries or APIs. Document why you implemented ugly solution. Include link to bug report or issue tracker. Example: "Workaround for React 17 bug where useEffect runs twice in StrictMode. Remove when upgrading to React 18. See: github.com/facebook/react/issues/24502." This context prevents future refactoring that removes necessary workaround.
Document assumptions and preconditions. If function assumes input is sorted, comment it. If code depends on external state, explain dependency. Example: "Assumes database connection already established. Call connectDB() first." Explicit assumptions prevent misuse and make debugging faster when assumptions violate.
Comment public APIs thoroughly. Public functions need docstring format comments explaining parameters, return values, exceptions, and usage examples. Users of your API cannot read implementation. Comments are their documentation. Private functions need fewer comments because readers can examine usage within same codebase. But complex private functions still deserve explanation.
What Comment Formats Should You Use?
Use documentation generation formats for public APIs. JSDoc for JavaScript, Javadoc for Java, Python docstrings, etc. These formats enable auto-generated documentation from comments. Structure: description, parameters with types, return value, exceptions thrown, usage example. Consistent format makes APIs professional and enables tooling. IDEs show docstrings as tooltips improving developer experience.
Inline comments should be brief, usually single line. Place above code they explain, not at end of line (unless very short). End-of-line comments clutter and wrap poorly. Bad: `const result = calculateComplexThing(data); // Calculates the complex thing`. Good: `// Convert user timezone to UTC for database storage const utcDate = convertToUTC(userDate, timezone);`. Comment above code flows naturally during reading.
TODO comments mark incomplete work. Format: `// TODO: Add input validation` or `// TODO(username): Refactor to use async/await`. Include your username if team is large. TODOs are searchable making incomplete work trackable. Some teams use FIXME for bugs, HACK for temporary solutions, NOTE for important information. Consistent prefixes enable grep for different comment types.
Warning comments flag dangerous or surprising code. `// WARNING: Modifying this breaks production deploy script` or `// DANGER: This deletes user data without confirmation`. Strong language justified for critical code. Warnings prevent careless changes with serious consequences. Future developers (or you in six months) appreciate warnings catching attention before modifying risky code.
What Should You Avoid in Comments?
Never write obvious comments that repeat code. `// Get user` above `getUser()` wastes space. Obvious comments train developers to ignore all comments. If most comments are useless, developers skip reading them. Then they miss actually useful comments. Only comment when adding information code does not convey. Quality over quantity always.
Avoid outdated comments that contradict code. Code changes but comments stay unchanged. Outdated comments are worse than no comments because they mislead. Comment says function returns string but code returns object. Developer trusts comment and code breaks. Update comments during code review. Delete comments rather than leave them outdated. Incorrect documentation causes bugs.
Do not use comments to explain bad code. If code needs extensive explanation, refactor it for clarity. Extract complex expressions into well-named variables. Break long functions into smaller ones with descriptive names. Self-documenting code beats commented confusing code. Comment explains why, not what. If commenting what code does, code is probably too complex.
Avoid commenting out code. Version control stores old code. Commented code clutters and confuses. Is it intentionally disabled or forgotten? Should it be removed? Git preserves history. Delete dead code. If you might need it, git log recovers it. Commented code eventually becomes confusing archaeological artifact nobody dares remove.
How Should You Comment Different Code Types?
Functions need header comments explaining purpose, parameters, return value, and side effects. Example: "Validates user email format and checks if email is already registered. Returns true if email is valid and available, false otherwise. Throws NetworkError if database unreachable." This complete description enables using function without reading implementation.
Classes need comments explaining responsibility and key concepts. Example: "UserManager handles user authentication, authorization, and profile management. Maintains in-memory cache of active sessions. Thread-safe for concurrent access." Class comment provides high-level understanding before diving into method details.
Complex conditionals need comments explaining business logic. Example: "Premium users get unlimited storage. Free users get 5GB. Trial users get 10GB for 30 days then downgrade to free tier." This comment explains business rules that code enforces. Without it, developers might see nested conditions but not understand underlying logic.
Configuration and constants need comments explaining purpose and constraints. Example: `const MAX_RETRIES = 3; // More than 3 retries indicates systemic issue requiring investigation`. Comment explains why this particular value was chosen. Future developers understand constraint before changing it.
How Can You Maintain Comment Quality?
Review comments during code review alongside code. Check comments for clarity, accuracy, and usefulness. Comments are part of code quality, not optional extra. Treat outdated or confusing comments as bugs requiring fixes. Code review is opportunity to ensure comments add value.
Refactor comments when refactoring code. Moving code without updating comments creates confusion. Extracting function should also extract relevant comments. Deleting code should delete associated comments. Keep comments and code synchronized through disciplined refactoring practice. Treat comments as integral to code structure.
Use linters to enforce documentation requirements. Tools like ESLint can require JSDoc comments on public functions. Automated checks ensure new code includes necessary documentation. Prevention is easier than remediation. Establish commenting standards early and enforce them consistently through tooling.
Periodically review comments for staleness. Schedule quarterly comment audits for critical codebases. Read comments checking if they still match code. Update or delete stale comments. This maintenance prevents comment rot where large portions of comments become misleading over time. Fresh, accurate comments are valuable. Stale comments are dangerous.
What Are Alternatives to Comments?
Self-documenting code through clear naming is best documentation. Variable named `isUserAuthenticated` needs no comment. Function named `calculateMonthlyInterestRate` is self-explanatory. Invest time choosing descriptive names. Good names eliminate need for many comments. When struggling to name something, you might not understand it well enough. Clear understanding enables clear naming.
Type systems provide documentation through types. TypeScript interface documenting function parameters makes comments redundant. Type signatures communicate expectations precisely. Modern statically-typed languages offer documentation through types. Comments still needed for why and business logic, but types handle what.
Tests serve as documentation showing usage examples. Well-written tests demonstrate how to call functions, what input is valid, and what output to expect. Tests are executable documentation that cannot get outdated without failing. For public APIs, comprehensive tests are as important as comments for documentation.
Code comments explain what code cannot: context, reasoning, and business logic. Write comments explaining why and when, not what and how. Good comments are concise, accurate, and genuinely helpful. They save time, prevent bugs, and enable effective collaboration. Invest in writing comments that future you will appreciate. Use River's tools to generate clean inline documentation for your code.