Self-Documenting Code vs. Comments: Lessons from Maintaining Large-Scale Codebases
Source: Dev.to
The Danger of Stale Comments
The primary risk of comments is obsolescence. When code is refactored, developers often forget to update the associated comments. Over time those comments “rot,” becoming misleading and dangerous. Self‑documenting code avoids this problem because the logic itself is the source of truth—if the logic changes, the “documentation” (the code) must change by necessity.
Pillars of Self‑Documenting Code
1. Avoid Generic Abbreviations
Names should convey the what and how without needing a side‑note.
// Bad
const d = 86400; // seconds in a day
// Good
const SECONDS_IN_A_DAY = 86400;
2. Encapsulate Conditionals
Complex logic inside an if statement adds cognitive load. Move the logic into a well‑named variable or function.
// Bad
if (user.age > 18 && user.hasSubscription && !user.isBanned) {
// …
}
// Good
const canAccessPremiumContent =
user.isAdult && user.hasActivePlan && !user.isAccountFlagged;
if (canAccessPremiumContent) {
// …
}
3. Use Strong Types as Living Documentation
Enums and interfaces replace “magic strings” and make valid options explicit.
// Dart example
enum OrderStatus { pending, shipped, delivered }
// Instead of passing a raw String for status, use OrderStatus.
When to Use Comments
Comments are valuable when the code cannot explain the context or why a decision was made. Typical scenarios include:
- Workarounds – e.g.,
// Using a legacy loop here because the .map() polyfill fails in IE11. - Business Logic Why – e.g.,
// We trigger this sync twice to ensure the third‑party API acknowledges the handshake. - Critical Warnings – e.g.,
// CRITICAL: Do not change the order of these calls; it will cause a deadlock in the database.
Comparison: Pros and Cons
| Approach | Pros | Cons |
|---|---|---|
| Self‑Documenting | • Always in sync with logic • Reduces visual noise • Forces better naming | • Cannot explain external “why” • May lead to very long variable names |
| Comments | • Explains non‑obvious business rules • Provides IDE tooltips (e.g., JSDoc/DartDoc) | • High maintenance cost • Prone to “rotting” • Often hides “smelly” code |
Documentation Hierarchy
-
Level 1 – The Logic
Write clean, modular code. If you need a comment to explain “Step 1, Step 2,” the function is probably too big—split it. -
Level 2 – The Types
Use TypeScript, Dart, or similar type systems to define the shape of your data. -
Level 3 – The Doc‑String
Add JSDoc/DartDoc for public APIs to surface information in IDE tooltips. -
Level 4 – The Why‑Comment
Add a comment only when a developer would still ask, “Why on earth did we do it this way?” after reading the clean code.
Conclusion
The goal of a senior engineer isn’t to write no comments, but to write no redundant comments. Every line of text in your codebase should add value. When the code is clean, comments can focus on high‑level strategy rather than low‑level syntax, making the whole system easier to understand, maintain, and evolve.