Comments and formatting skill

Comprehensive guide to comment discipline and code formatting.

by wondelai·MIT license·★ 2,235 Stars on the repo·GitHub ↗

Use now

Files of Comments and formatting

wondelai/main1 file
comments-formatting.md
Show the full text406 lines

Comments and Formatting

Comprehensive guide to comment discipline and code formatting. Based on Robert C. Martin's Clean Code, Chapters 4 and 5.

Table of Contents

  1. The Truth About Comments
  2. Good Comments
  3. Bad Comments
  4. Formatting
  5. When Comments Are Truly Necessary

The Truth About Comments

Don't comment bad code -- rewrite it. Comments are, at best, a necessary evil. The proper use of comments is to compensate for our failure to express ourselves in code. Every time you write a comment, you should grimace and feel the failure of your ability of expression.

Comments lie. Not always, and not intentionally, but too often. Code changes and evolves; comments don't always follow. The older a comment is and the farther it is from the code it describes, the more likely it is to be wrong.


Good Comments

Not all comments are bad. Some are necessary and valuable. Here are the types worth writing:

Copyright and license headers mandated by corporate or legal standards.

// Copyright (c) 2024 Acme Corp. All rights reserved.
// Licensed under the Apache License, Version 2.0

Keep them short. Reference a standard license file rather than embedding the full text.

Informative Comments

Provide information that cannot be expressed in the code itself.

// Format: kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");

Even here, a named constant or custom type could eliminate the need: TIMESTAMP_PATTERN.

Explanation of Intent

Explain why a decision was made, not what the code does.

# We sort by creation date descending because the business requirement
# specifies that the most recently created items appear first in the
# dashboard, even though alphabetical would be more intuitive.
items.sort(key=lambda x: x.created_at, reverse=True)

This is valuable because the what is visible in the code, but the why would otherwise be lost.

Warning of Consequences

Alert other developers about consequences that are not obvious.

// Don't run this test in CI -- it takes 45 minutes and requires
// a live connection to the production payment gateway
@Ignore("Long-running integration test requiring production access")
public void testLivePaymentGateway() { ... }
TODO Comments

Mark work that needs to be done but cannot be done right now.

# TODO(#1234): Replace with proper caching once Redis is provisioned
def get_user_preferences(user_id):
    return db.query(f"SELECT * FROM preferences WHERE user_id = {user_id}")

Rules for TODOs:

  • Include a ticket number or issue reference
  • Scan and resolve them regularly (they are not permanent)
  • Never use TODO as an excuse to leave broken code
  • IDE/linter plugins can track and report outstanding TODOs
Amplification

Emphasize the importance of something that might otherwise seem inconsequential.

String listItemContent = match.group(3).trim();
// The trim is critically important. It removes trailing whitespace
// that would cause the item to be recognized as another list.
new ListItemWidget(this, listItemContent, this.level + 1);

Bad Comments

Most comments fall into these categories and should be eliminated:

Mumbling

Comments written because "you should comment" rather than because they add value.

// The processor
private Processor processor;

// Default constructor
public MyClass() { }

These say nothing. Delete them.

Redundant Comments

Comments that take longer to read than the code they describe.

// Returns the day of the month
public int getDayOfMonth() {
    return dayOfMonth;
}

// Check if the employee is eligible for benefits
if (employee.isEligibleForBenefits()) { ... }

The code already says exactly this. The comment is noise.

Misleading Comments

Comments that are subtly incorrect -- the most dangerous kind.

// Returns true if the user is active
public boolean isActive() {
    return lastLoginDate != null && !isDeleted && subscriptionEndDate.isAfter(now());
}

The comment says "active" means logged in before. The code checks three conditions. When the code changes and the comment doesn't, a future developer will be misled.

Mandated Comments

Rules that require every function or variable to have a comment produce noise.

/**
 * The name.
 * @param name The name.
 */
public void setName(String name) {
    this.name = name;
}

This adds no information. It's clutter that developers learn to ignore, which means they'll also ignore the few comments that actually matter.

Journal Comments

Changelog entries at the top of files.

// 2024-01-15 - Added validation for email format
// 2024-01-20 - Fixed bug in email regex
// 2024-02-01 - Added support for international emails

This is what version control is for. git log and git blame provide this information with more accuracy and context.

Commented-Out Code
# user_cache = {}
# def get_cached_user(user_id):
#     if user_id not in user_cache:
#         user_cache[user_id] = db.get_user(user_id)
#     return user_cache[user_id]

def get_user(user_id):
    return db.get_user(user_id)

Other developers are afraid to delete commented-out code because they think it must be there for a reason. It accumulates like barnacles. Delete it. Version control has perfect memory.

Noise Comments

Comments that restate the obvious in a different form.

/** Default constructor */
protected AnnualDateRule() { }

/** The day of the month */
private int dayOfMonth;

/** Returns the day of the month
 * @return the day of the month */
public int getDayOfMonth() { return dayOfMonth; }

Every one of these is noise. They provide no information, train developers to ignore comments, and create a false sense of documentation thoroughness.

Position Markers and Banners
// ========== PRIVATE METHODS ==========
// --- Validation ---
// /////// Constructor ///////

If your file is so large that you need position markers, the file is too large. Extract classes instead of adding banners.

Closing Brace Comments
if (condition) {
    while (running) {
        for (item : items) {
            // ... lots of code ...
        } // for
    } // while
} // if

If you need comments to track closing braces, your function is too long. Extract methods until the structure is obvious.

Attribution Comments
// Added by: [email protected]

Version control tracks authorship more reliably. Use git blame.


Formatting

Why Formatting Matters

Code formatting is about communication, and communication is the professional developer's first order of business. The formatting of your code communicates important information long after the original developer has moved on.

Vertical Formatting
The Newspaper Metaphor

Source files should be organized like a newspaper article:

  • Name should be simple but explanatory (the headline)
  • Top should provide high-level concepts and algorithms (the synopsis)
  • Bottom should contain the lowest-level functions and details (the body)
Vertical Openness Between Concepts

Each group of related lines represents a complete thought. Separate thoughts with blank lines.

# GOOD: Blank lines separate concepts
import os
import sys

from myapp.models import User
from myapp.services import EmailService


class UserRegistration:

    def __init__(self, email_service):
        self.email_service = email_service

    def register(self, name, email):
        user = User.create(name=name, email=email)
        self.email_service.send_welcome(user)
        return user
# BAD: Everything runs together
import os
import sys
from myapp.models import User
from myapp.services import EmailService
class UserRegistration:
    def __init__(self, email_service):
        self.email_service = email_service
    def register(self, name, email):
        user = User.create(name=name, email=email)
        self.email_service.send_welcome(user)
        return user
Vertical Density

Lines that are tightly related should appear close together vertically. Don't insert blank lines between closely related lines.

// BAD: Useless comments break vertical density
public class ReporterConfig {

    /**
     * The class name of the reporter listener
     */
    private String className;

    /**
     * The properties of the reporter listener
     */
    private List<Property> properties = new ArrayList<>();
}

// GOOD: Dense, related declarations together
public class ReporterConfig {
    private String className;
    private List<Property> properties = new ArrayList<>();
}
Vertical Distance

Closely related concepts should be kept vertically close to each other. Don't force the reader to hop around the file.

Rules:

  • Local variables: Declare at the top of the function or as close to first usage as practical
  • Instance variables: Declare at the top of the class (everyone needs to know about them)
  • Dependent functions: The caller should be above the callee, and they should be close
  • Conceptual affinity: Functions that do similar things or operate on the same data should be near each other
Vertical Ordering

Function call dependencies should point downward: a function that is called should be below the function that calls it. This creates a nice flow from high-level to low-level, like reading a newspaper.

Horizontal Formatting
Line Length

Keep lines short. The old 80-character limit is a reasonable guideline. Modern screens can show more, but readability drops beyond 100-120 characters. Scrolling horizontally breaks the reader's flow.

Horizontal Openness and Density

Use whitespace to associate strongly related things and disassociate weakly related things.

// Spaces around assignment (weak association between sides)
int lineCount = countLines();

// No space between function name and parenthesis (strong association)
lineCount = countLines();

// Spaces around binary operators by precedence
return b*b - 4*a*c;  // Multiplication is higher precedence, tighter
return (-b + determinant) / (2*a);
Indentation

Indentation makes the scope hierarchy visible. Each level of nesting gets one indentation level. Never break this rule, even for short if statements or tiny loops.

// BAD: Collapsed scopes hide structure
if (condition) return true;

// GOOD: Indentation preserved
if (condition) {
    return true;
}
Team Rules

A team should agree on a single formatting style and everyone should use it. Individual style preferences must yield to the team standard.

The best way to enforce team rules:

Approach Tool examples Benefit
Automated formatter Prettier, Black, gofmt, rustfmt Eliminates all style debates
Linter with auto-fix ESLint, Pylint, RuboCop Catches style and quality issues
Pre-commit hooks Husky, pre-commit, lefthook Prevents style violations from entering repo
CI enforcement Format check in pipeline Catches anything hooks miss
EditorConfig .editorconfig file Consistent settings across editors

The best formatting rule: Use an automated formatter and never think about formatting again. Time spent debating tabs versus spaces is time not spent writing clean code.


When Comments Are Truly Necessary

Despite the general advice to minimize comments, certain situations genuinely require them:

Situation Why code alone isn't enough Example
Regulatory requirement Law/compliance requires documentation HIPAA, SOX, GDPR compliance notes
Non-obvious performance choice Algorithm choice isn't self-evident "Using radix sort here because n > 10M and keys are bounded"
External system quirk Workaround for third-party bug "API returns 200 for errors; we check response body instead"
Concurrency rationale Threading decisions need explanation "Double-checked locking required here because..."
Domain formula Mathematical formula from spec "Amortization formula from IRS Publication 936"
Public API contract Users cannot read implementation Javadoc for library interfaces

The key: comments should explain why, never what. If you find yourself explaining what the code does, the code needs to be clearer, not the comment.

1# Comments and Formatting
2 
3Comprehensive guide to comment discipline and code formatting. Based on Robert C. Martin's *Clean Code*, Chapters 4 and 5.
4 
5 
6## Table of Contents
71. [The Truth About Comments](#the-truth-about-comments)
82. [Good Comments](#good-comments)
93. [Bad Comments](#bad-comments)
104. [Formatting](#formatting)
115. [When Comments Are Truly Necessary](#when-comments-are-truly-necessary)
12 
13---
14 
15## The Truth About Comments
16 
17**Don't comment bad code -- rewrite it.** Comments are, at best, a necessary evil. The proper use of comments is to compensate for our failure to express ourselves in code. Every time you write a comment, you should grimace and feel the failure of your ability of expression.
18 
19Comments lie. Not always, and not intentionally, but too often. Code changes and evolves; comments don't always follow. The older a comment is and the farther it is from the code it describes, the more likely it is to be wrong.
20 
21---
22 
23## Good Comments
24 
25Not all comments are bad. Some are necessary and valuable. Here are the types worth writing:
26 
27### Legal Comments
28 
29Copyright and license headers mandated by corporate or legal standards.
30 
31```java
32// Copyright (c) 2024 Acme Corp. All rights reserved.
33// Licensed under the Apache License, Version 2.0
34```
35 
36Keep them short. Reference a standard license file rather than embedding the full text.
37 
38### Informative Comments
39 
40Provide information that cannot be expressed in the code itself.
41 
42```java
43// Format: kk:mm:ss EEE, MMM dd, yyyy
44Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");
45```
46 
47Even here, a named constant or custom type could eliminate the need: `TIMESTAMP_PATTERN`.
48 
49### Explanation of Intent
50 
51Explain *why* a decision was made, not *what* the code does.
52 
53```python
54# We sort by creation date descending because the business requirement
55# specifies that the most recently created items appear first in the
56# dashboard, even though alphabetical would be more intuitive.
57items.sort(key=lambda x: x.created_at, reverse=True)
58```
59 
60This is valuable because the *what* is visible in the code, but the *why* would otherwise be lost.
61 
62### Warning of Consequences
63 
64Alert other developers about consequences that are not obvious.
65 
66```java
67// Don't run this test in CI -- it takes 45 minutes and requires
68// a live connection to the production payment gateway
69@Ignore("Long-running integration test requiring production access")
70public void testLivePaymentGateway() { ... }
71```
72 
73### TODO Comments
74 
75Mark work that needs to be done but cannot be done right now.
76 
77```python
78# TODO(#1234): Replace with proper caching once Redis is provisioned
79def get_user_preferences(user_id):
80 return db.query(f"SELECT * FROM preferences WHERE user_id = {user_id}")
81```
82 
83**Rules for TODOs:**
84- Include a ticket number or issue reference
85- Scan and resolve them regularly (they are not permanent)
86- Never use TODO as an excuse to leave broken code
87- IDE/linter plugins can track and report outstanding TODOs
88 
89### Amplification
90 
91Emphasize the importance of something that might otherwise seem inconsequential.
92 
93```java
94String listItemContent = match.group(3).trim();
95// The trim is critically important. It removes trailing whitespace
96// that would cause the item to be recognized as another list.
97new ListItemWidget(this, listItemContent, this.level + 1);
98```
99 
100---
101 
102## Bad Comments
103 
104Most comments fall into these categories and should be eliminated:
105 
106### Mumbling
107 
108Comments written because "you should comment" rather than because they add value.
109 
110```java
111// The processor
112private Processor processor;
113 
114// Default constructor
115public MyClass() { }
116```
117 
118These say nothing. Delete them.
119 
120### Redundant Comments
121 
122Comments that take longer to read than the code they describe.
123 
124```java
125// Returns the day of the month
126public int getDayOfMonth() {
127 return dayOfMonth;
128}
129 
130// Check if the employee is eligible for benefits
131if (employee.isEligibleForBenefits()) { ... }
132```
133 
134The code already says exactly this. The comment is noise.
135 
136### Misleading Comments
137 
138Comments that are subtly incorrect -- the most dangerous kind.
139 
140```java
141// Returns true if the user is active
142public boolean isActive() {
143 return lastLoginDate != null && !isDeleted && subscriptionEndDate.isAfter(now());
144}
145```
146 
147The comment says "active" means logged in before. The code checks three conditions. When the code changes and the comment doesn't, a future developer will be misled.
148 
149### Mandated Comments
150 
151Rules that require every function or variable to have a comment produce noise.
152 
153```java
154/**
155 * The name.
156 * @param name The name.
157 */
158public void setName(String name) {
159 this.name = name;
160}
161```
162 
163This adds no information. It's clutter that developers learn to ignore, which means they'll also ignore the few comments that actually matter.
164 
165### Journal Comments
166 
167Changelog entries at the top of files.
168 
169```java
170// 2024-01-15 - Added validation for email format
171// 2024-01-20 - Fixed bug in email regex
172// 2024-02-01 - Added support for international emails
173```
174 
175This is what version control is for. `git log` and `git blame` provide this information with more accuracy and context.
176 
177### Commented-Out Code
178 
179```python
180# user_cache = {}
181# def get_cached_user(user_id):
182# if user_id not in user_cache:
183# user_cache[user_id] = db.get_user(user_id)
184# return user_cache[user_id]
185 
186def get_user(user_id):
187 return db.get_user(user_id)
188```
189 
190Other developers are afraid to delete commented-out code because they think it must be there for a reason. It accumulates like barnacles. **Delete it.** Version control has perfect memory.
191 
192### Noise Comments
193 
194Comments that restate the obvious in a different form.
195 
196```java
197/** Default constructor */
198protected AnnualDateRule() { }
199 
200/** The day of the month */
201private int dayOfMonth;
202 
203/** Returns the day of the month
204 * @return the day of the month */
205public int getDayOfMonth() { return dayOfMonth; }
206```
207 
208Every one of these is noise. They provide no information, train developers to ignore comments, and create a false sense of documentation thoroughness.
209 
210### Position Markers and Banners
211 
212```java
213// ========== PRIVATE METHODS ==========
214// --- Validation ---
215// /////// Constructor ///////
216```
217 
218If your file is so large that you need position markers, the file is too large. Extract classes instead of adding banners.
219 
220### Closing Brace Comments
221 
222```java
223if (condition) {
224 while (running) {
225 for (item : items) {
226 // ... lots of code ...
227 } // for
228 } // while
229} // if
230```
231 
232If you need comments to track closing braces, your function is too long. Extract methods until the structure is obvious.
233 
234### Attribution Comments
235 
236```java
237// Added by: [email protected]
238```
239 
240Version control tracks authorship more reliably. Use `git blame`.
241 
242---
243 
244## Formatting
245 
246### Why Formatting Matters
247 
248Code formatting is about communication, and communication is the professional developer's first order of business. The formatting of your code communicates important information long after the original developer has moved on.
249 
250### Vertical Formatting
251 
252#### The Newspaper Metaphor
253 
254Source files should be organized like a newspaper article:
255- **Name** should be simple but explanatory (the headline)
256- **Top** should provide high-level concepts and algorithms (the synopsis)
257- **Bottom** should contain the lowest-level functions and details (the body)
258 
259#### Vertical Openness Between Concepts
260 
261Each group of related lines represents a complete thought. Separate thoughts with blank lines.
262 
263```python
264# GOOD: Blank lines separate concepts
265import os
266import sys
267 
268from myapp.models import User
269from myapp.services import EmailService
270 
271 
272class UserRegistration:
273 
274 def __init__(self, email_service):
275 self.email_service = email_service
276 
277 def register(self, name, email):
278 user = User.create(name=name, email=email)
279 self.email_service.send_welcome(user)
280 return user
281```
282 
283```python
284# BAD: Everything runs together
285import os
286import sys
287from myapp.models import User
288from myapp.services import EmailService
289class UserRegistration:
290 def __init__(self, email_service):
291 self.email_service = email_service
292 def register(self, name, email):
293 user = User.create(name=name, email=email)
294 self.email_service.send_welcome(user)
295 return user
296```
297 
298#### Vertical Density
299 
300Lines that are tightly related should appear close together vertically. Don't insert blank lines between closely related lines.
301 
302```java
303// BAD: Useless comments break vertical density
304public class ReporterConfig {
305 
306 /**
307 * The class name of the reporter listener
308 */
309 private String className;
310 
311 /**
312 * The properties of the reporter listener
313 */
314 private List<Property> properties = new ArrayList<>();
315}
316 
317// GOOD: Dense, related declarations together
318public class ReporterConfig {
319 private String className;
320 private List<Property> properties = new ArrayList<>();
321}
322```
323 
324#### Vertical Distance
325 
326Closely related concepts should be kept vertically close to each other. Don't force the reader to hop around the file.
327 
328**Rules:**
329- **Local variables:** Declare at the top of the function or as close to first usage as practical
330- **Instance variables:** Declare at the top of the class (everyone needs to know about them)
331- **Dependent functions:** The caller should be above the callee, and they should be close
332- **Conceptual affinity:** Functions that do similar things or operate on the same data should be near each other
333 
334#### Vertical Ordering
335 
336Function call dependencies should point downward: a function that is called should be below the function that calls it. This creates a nice flow from high-level to low-level, like reading a newspaper.
337 
338### Horizontal Formatting
339 
340#### Line Length
341 
342**Keep lines short.** The old 80-character limit is a reasonable guideline. Modern screens can show more, but readability drops beyond 100-120 characters. Scrolling horizontally breaks the reader's flow.
343 
344#### Horizontal Openness and Density
345 
346Use whitespace to associate strongly related things and disassociate weakly related things.
347 
348```java
349// Spaces around assignment (weak association between sides)
350int lineCount = countLines();
351 
352// No space between function name and parenthesis (strong association)
353lineCount = countLines();
354 
355// Spaces around binary operators by precedence
356return b*b - 4*a*c; // Multiplication is higher precedence, tighter
357return (-b + determinant) / (2*a);
358```
359 
360#### Indentation
361 
362Indentation makes the scope hierarchy visible. Each level of nesting gets one indentation level. **Never break this rule, even for short `if` statements or tiny loops.**
363 
364```java
365// BAD: Collapsed scopes hide structure
366if (condition) return true;
367 
368// GOOD: Indentation preserved
369if (condition) {
370 return true;
371}
372```
373 
374### Team Rules
375 
376**A team should agree on a single formatting style and everyone should use it.** Individual style preferences must yield to the team standard.
377 
378The best way to enforce team rules:
379 
380| Approach | Tool examples | Benefit |
381|----------|---------------|---------|
382| **Automated formatter** | Prettier, Black, gofmt, rustfmt | Eliminates all style debates |
383| **Linter with auto-fix** | ESLint, Pylint, RuboCop | Catches style and quality issues |
384| **Pre-commit hooks** | Husky, pre-commit, lefthook | Prevents style violations from entering repo |
385| **CI enforcement** | Format check in pipeline | Catches anything hooks miss |
386| **EditorConfig** | `.editorconfig` file | Consistent settings across editors |
387 
388**The best formatting rule:** Use an automated formatter and never think about formatting again. Time spent debating tabs versus spaces is time not spent writing clean code.
389 
390---
391 
392## When Comments Are Truly Necessary
393 
394Despite the general advice to minimize comments, certain situations genuinely require them:
395 
396| Situation | Why code alone isn't enough | Example |
397|-----------|---------------------------|---------|
398| **Regulatory requirement** | Law/compliance requires documentation | HIPAA, SOX, GDPR compliance notes |
399| **Non-obvious performance choice** | Algorithm choice isn't self-evident | "Using radix sort here because n > 10M and keys are bounded" |
400| **External system quirk** | Workaround for third-party bug | "API returns 200 for errors; we check response body instead" |
401| **Concurrency rationale** | Threading decisions need explanation | "Double-checked locking required here because..." |
402| **Domain formula** | Mathematical formula from spec | "Amortization formula from IRS Publication 936" |
403| **Public API contract** | Users cannot read implementation | Javadoc for library interfaces |
404 
405The key: comments should explain *why*, never *what*. If you find yourself explaining *what* the code does, the code needs to be clearer, not the comment.
406 

Discussion

Alternatives