naming-and-boundaries

verified

c28edc1f-2a25-4c30-a039-7906d90b4971

Name functions, variables, and classes for intent and keep each unit single-responsibility. Use when code is hard to read or a function keeps growing.

Metadata

Skill ID
c28edc1f-2a25-4c30-a039-7906d90b4971
Version
1
Owner
387274b7-2891-478b-81b8-e11d5adb9319
Tags
namingreadabilitysingle-responsibilitysrpclean-codefunctions
Signature
verified
Integrity
OK
Content hash
bd6d75ab312470924846c12626bde4d484388194d5173459e5845d78e3bb3d32
Created
2026-08-15T05:27:00Z

Skill file

Raw skill file (markdown source)
# Naming and Boundaries

Use when code is hard to read, a function keeps growing, or you cannot tell what a
piece of code does from its name. Good names and small single-responsibility units
are the cheapest maintainability win available.

## 1. Naming rules

| Kind | Rule | Good | Bad |
|---|---|---|---|
| Function | verb phrase — what it *does* | `calculate_total()` | `total()` |
| Class | noun — what it *is* | `Invoice` | `ProcessInvoice` |
| Boolean | `is_`/`has_`/`can_` predicate | `is_expired` | `check()` |
| Collection | plural noun | `orders` | `order_list` |
| Constant | `UPPER_SNAKE_CASE` | `MAX_RETRIES` | `maxRetries` |

Banned names — they convey no information:

```python
data, info, tmp, temp, handle, do, process, manage, thing, stuff, util,
helper, manager, get (without an object), result (without qualification)
```

Replace each with the *specific* thing: `user_input` not `data`, `invoices` not
`stuff`, `fetch_user` not `get`.

## 2. The 10-line function heuristic

If a function exceeds ~10 lines (excluding docstring and blank lines), it is doing
more than one thing. **Extract until the intent reads as prose:**

```python
# BEFORE — 20 lines, three responsibilities, unclear intent
def handle(event):
    # validate...
    if not event.get("id"):
        return 400
    # fetch...
    user = db.query(User).filter_by(id=event["id"]).first()
    # notify...
    send_email(user.email, "welcome")
    return 200

# AFTER — intent reads as prose
def handle(event):
    if not is_valid(event):
        return 400
    user = fetch_user(event["id"])
    notify_welcome(user)
    return 200
```

The test: read the function body aloud. If it reads like a sequence of clear
sentences, you have the right boundaries.

## 3. Parameters-as-a-smell

More than 3 positional arguments is a signal that parameters belong together:

```python
# SMELL — 5 positional args, easy to mis-order
def create_user(name, email, age, country, plan, is_admin): ...

# FIX — group into a dataclass/dict
@dataclass(frozen=True)
class NewUser:
    name: str
    email: str
    age: int
    country: str
    plan: str
    is_admin: bool = False

def create_user(user: NewUser) -> User: ...
```

Also prefer **keyword-only** args for optional parameters to prevent mis-ordering:

```python
def send(to: str, *, cc: list[str] = [], retries: int = 3): ...
```

## 4. Extracting — the mechanics

Use your IDE's refactor, never `sed`:

```bash
# PyCharm: Ctrl+Alt+M (extract method), Shift+F6 (rename)
# VS Code: Ctrl+. -> "Extract to function" / "Rename Symbol"
# Vim/CLI: rope or jedi rename, or do it manually + fix callers by grep
```

After a rename, grep for stale callers and run the tests:

```bash
rg 'old_name' .          # find any caller you missed
pytest -q                # prove behavior is unchanged
```

## 5. Renaming in a large codebase (migration playbook)

Renaming a symbol that has many callers is a mechanical, safe operation if you
sequence it right:

```bash
# 1. Find every reference first
rg -n 'old_name' src/ tests/

# 2. Rename via IDE refactor (or a single careful sed if no IDE)
#    PyCharm: Shift+F6 | VS Code: F2 | vim: :%s/\bold_name\b/new_name/gc

# 3. Confirm zero stale references remain
rg -n '\bold_name\b' src/ tests/   # expect: no output

# 4. Prove behavior unchanged
pytest -q

# 5. Commit the rename alone (no behavior change mixed in)
git commit -m "Rename old_name -> new_name"
```

The discipline: rename in its own commit, verify with `rg` + tests, and never mix
a rename with a behavior change (that makes the diff unreviewable).

## 6. Boundaries beyond functions — modules and classes

Single responsibility applies at every level, not just functions:

| Level | "One thing" means | Splits when |
|---|---|---|
| Function | One operation | > ~10 lines or "and" in the name |
| Class | One concept + its data | Two unrelated field groups |
| Module | One cohesive set of related functions/classes | Importing it drags in unrelated deps |

A module named `utils.py` or `common.py` is a boundary failure — it has no "one
thing." Split it into purpose-named modules (`time_utils.py`, `string_utils.py`,
`http_client.py`).

## Guardrails

- Do **not** rename with `sed`/find-replace — it misses dynamic references and
  breaks callsites. Use IDE refactor or verify with `rg` + tests.
- Do **not** use vague-but-short names (`f`, `x`, `val`) just to save typing — the
  reader pays for it.
- Do **not** substitute comments for names. If you need `# x is the user count`,
  rename `x` to `user_count`.
- Do **not** extract so aggressively that you create 5-line wrappers with no
  independent meaning.
- Keep one responsibility per function/class — if the name needs "and" ("fetch
  *and* save"), it is two functions.

## Pitfalls

- **Renaming without updating callers** — the top source of "it worked yesterday"
  breakage; always `rg` + run tests after a rename.
- **Vague-but-short names** — `d`, `e`, `res` are worse than slightly longer
  descriptive names.
- **Comments as name-substitutes** — a comment explaining `data` should be a better
  variable name.
- **Over-extraction** — 30 one-line functions is not readable, it is fragmentation.
- **`util`/`helper`/`manager` junk drawers** — names that mean nothing attract
  everything; split them by responsibility.

## Verify / Checklist

- [ ] Every function is a verb phrase; every class is a noun; booleans are `is_`/`has_`/`can_`.
- [ ] No banned names (`data`, `info`, `tmp`, `handle`, `util`, `manager`) without strong justification.
- [ ] No function exceeds ~10 lines of logic (excluding docstring/blank lines).
- [ ] No function/method has more than 3 positional parameters.
- [ ] A rename was followed by `rg old_name` to confirm no stale callers.
- [ ] Tests pass after any rename/extract (behavior is unchanged).
- [ ] Reading a function body aloud produces clear, discrete sentences.

Attached files

No attached files.