Files
playbook/antigravity-awesome-skills/plugins/agentic-awesome-skills/skills/clean-code-guard/references/naming-and-functions.md
T
2026-07-18 00:02:59 +00:00

188 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Naming and Functions — Clean Code Chapters 2 and 3
Source: Robert C. Martin, *Clean Code*. Sample chapters online at the Pearson PDF; chapter summaries at Vivek Khatri's Ch. 2 notes and Herberto Graça's Ch. 3 summary.
## Contents
- Meaningful names
- N1. Intention-revealing
- N2. No disinformation, no encodings
- N3. Meaningful distinctions
- N4. Searchable, pronounceable
- N5. Class names are nouns, method names are verbs
- N6. Banned generic names
- Functions
- F1. Small. Then smaller.
- F2. Do one thing
- F3. One level of abstraction per function
- F4. Step-down rule
- F5. Few arguments
- F6. No flag arguments
- F7. No output arguments / Command-Query Separation
- F8. No side effects in queries
- F9. Prefer exceptions to return codes
- F10. Duplication is the root evil
- Self-check for naming and functions
## Meaningful names
### N1. Intention-revealing
A name should tell you *why it exists, what it does, and how it's used*. If you need a comment to explain a name, the name is wrong.
**Bad:**
```text
d // elapsed time in days
ts = []
fn(xs)
```
**Good:**
```text
elapsedDays
timestamps
filterOverdueInvoices(invoices)
```
### N2. No disinformation, no encodings
No Hungarian notation (`strName`, `iCount`). No interface-prefix `I` (`IUserService`). No member prefix `m_`. No "List" suffix unless the type is actually a list (`accountList` for a `set` is disinformation).
**Bad:** `strFirstName`, `IUserRepo`, `m_count`, `userArray` (when it is not an array).
**Good:** `first_name`, `UserRepo`, `count`, `users_by_id`.
### N3. Meaningful distinctions
Do not differentiate names by adding noise words. `ProductInfo`, `ProductData`, `Product` — what's the difference? Same with `getActiveAccount` vs. `getActiveAccountInfo`. If the distinction is real, name the distinction.
### N4. Searchable, pronounceable
Single-letter names are acceptable inside short loop scope (`for i in range(...)`). Anywhere else they hurt grep. `MAX_RETRIES` is searchable; `7` is not.
If you can't read the name aloud in a code review, it's a bad name. `genymdhms` is a real-world example from the book — `generation_timestamp` is the fix.
### N5. Class names are nouns, method names are verbs
`User`, `Invoice`, `Account` — classes are things. `saveInvoice`, `computeTotal`, `notifyUser` — methods are actions. A class named `ProcessInvoice` and a method named `Invoice` are both wrong.
### N6. Banned generic names
Without a qualifier, these names always violate intention-revealing:
- `data`, `data2`, `data_final`
- `result`, `result_final`
- `item`, `value`, `temp`, `obj`, `info`
- `helper`, `manager`, `utils`, `common`
- `handle_*`, `process_*`, `do_*` (when `*` is also generic)
Qualified versions are fine: `raw_csv_bytes`, `parsed_invoice`, `dedup_by_email`.
---
## Functions
### F1. Small. Then smaller.
Target ≤20 lines. Uncle Bob's harder pass says 24 lines is the goal. If a function does not fit on a screen, it does too much. Extract.
### F2. Do one thing
A function does one thing when you cannot extract another function from it with a name that is not a restatement of its body. If `compute_invoice` contains a 10-line block that you could meaningfully call `apply_discount`, the original was doing more than one thing.
### F3. One level of abstraction per function
Mixing levels is the most common subtle defect. Do not put an HTTP call, a SQL query, a regex parse, and a business rule in the same function — those are four levels.
**Bad:**
```text
renderUserReport(userId):
connection = openDatabaseConnection()
row = queryUserRow(connection, userId)
displayName = row.firstName + " " + row.lastName
markup = "<h1>" + displayName + "</h1>"
return markup
```
Four levels: connection, query, formatting, presentation.
**Good:**
```text
renderUserReport(userId):
user = userRepository.findById(userId)
return userReportView.render(user)
```
### F4. Step-down rule
Read a file top-to-bottom; each function is followed by functions one level of abstraction below. Callers above callees. Confirmed by Uncle Bob himself on X.
### F5. Few arguments
Zero is best. One is fine. Two is OK. Three "should be avoided." Four or more "requires very special justification" — usually means you should pass a config object.
At five parameters, stop and extract a request/config object: record, struct, DTO, or equivalent.
### F6. No flag arguments
A boolean parameter that switches behavior is always wrong. Split into two functions.
**Bad:**
```text
render(invoice, asHtml):
if asHtml:
...
else:
...
```
**Good:**
```text
renderInvoiceHtml(invoice)
renderInvoicePdf(invoice)
```
The same applies to `mode="x"` string enums when the mode changes behavior. If `mode` parameterizes data (locale, currency), it's fine. If it parameterizes *which function runs*, split.
### F7. No output arguments / Command-Query Separation
A function either returns a value (query) or has a side effect (command). Never both.
**Bad:**
```text
save(record) -> boolean
// Returns true if saved, false if record was not found.
```
What does the bool mean? Success? Found-ness? The caller can't tell.
**Good:**
```text
save(record)
recordExists(recordId) -> boolean
```
### F8. No side effects in queries
A getter-style, finder-style, or predicate-style function must not mutate state. If it caches, log the cache write at debug level; do not change observable behavior.
### F9. Prefer exceptions to return codes
`if save(x):` is a code smell. Either save succeeds (returns nothing) or it raises (`InvoiceSaveError`). Return codes proliferate up the call stack and get forgotten; exceptions can't be ignored silently.
### F10. Duplication is the root evil
If two functions share a non-trivial block, extract it. But — see [dry-kiss-yagni.md](dry-kiss-yagni.md) for when this is wrong (Sandi Metz's "wrong abstraction" caveat).
---
## Self-check for naming and functions
Before you ship code:
1. Do all names answer "what does this represent" without a comment?
2. Are functions ≤20 lines?
3. Are functions doing one thing? (Can you extract another function with a non-restating name? If yes, you're doing more than one.)
4. Are mixed abstraction levels eliminated?
5. Are there any functions with >4 parameters? Extract a config object.
6. Are there boolean flag arguments? Split.
7. Do any functions both return a value *and* mutate state in a way callers depend on? Split.