docs(coding): files 1000 lines max, functions 200 lines max, no earlier split point
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e385e01993
commit
44c2d1502c
4 files changed
+26
-25
No files matched your search
@@ -139,9 +139,9 @@ Attach an `AbortController` to every long-lived `fetch` and abort it in cleanup.
|
||||
- **Imports**: prefer the configured alias (`@/…`) over relative climbs.
|
||||
- Indentation and quote/semicolon style: **match the file you are editing**. Do not introduce
|
||||
a new formatter or lint config into a project that already has one.
|
||||
- **One file until it earns a split** — around 250 lines for a self-contained plugin/feature,
|
||||
or when a second file is genuinely reusable (`utils.ts`, `types.ts`, `components/`).
|
||||
Hard limits from the general rules still apply: ESLint's `max-lines` and
|
||||
- **One file until it earns a split** — when it nears the 1000-line limit, or when a second
|
||||
file is genuinely reusable (`utils.ts`, `types.ts`, `components/`).
|
||||
The limits from the general rules (1000 lines per file, 200 per function) apply: ESLint's `max-lines` and
|
||||
`max-lines-per-function` cover both natively, so prefer those over a separate test here.
|
||||
|
||||
---
|
||||
|
||||
@@ -225,9 +225,8 @@ When talking to a rate-limited API (a chat platform, a store, a tracker):
|
||||
function-length rule — `PLR0915` (`too-many-statements`, default 50) and `C901`
|
||||
(cyclomatic complexity) are the closest proxies and are worth enabling, but neither
|
||||
measures lines. Put **both** the function-length and file-length limits in a pytest
|
||||
case (see [`coding-dev.md` §4](skills-public/coding-dev.md)): file hard 1000 / soft 600 (soft
|
||||
prints a note rather than failing), function hard 100 measured by walking the `ast` rather than
|
||||
counting lines of text. **Guard the guard** with a test asserting the glob matched anything at
|
||||
case (see [`coding-dev.md` §4](skills-public/coding-dev.md)): file 1000 lines max, function
|
||||
200 lines max measured by walking the `ast` rather than counting lines of text. **Guard the guard** with a test asserting the glob matched anything at
|
||||
all — a pattern that matches nothing makes every limit vacuous and still passes.
|
||||
|
||||
---
|
||||
|
||||
@@ -73,8 +73,12 @@ workspace = true
|
||||
wrong for the *entire* codebase — not a shortcut for one stubborn call site. It silences
|
||||
the rule in code nobody has written yet.
|
||||
- **Re-test every `#[allow]` periodically.** If removing one produces no warning, delete it.
|
||||
- `too_many_lines` is the mechanical enforcement of the 100-line function limit. A function
|
||||
that trips it wants extracting, not an allow.
|
||||
- `too_many_lines` is the mechanical enforcement of the 200-line function limit. Clippy's
|
||||
default threshold is 100, so set it in `clippy.toml`:
|
||||
```toml
|
||||
too-many-lines-threshold = 200
|
||||
```
|
||||
A function that trips it wants extracting, not an allow.
|
||||
- Clippy has **no file-length lint**, so the 1000-line file limit belongs in a test — see
|
||||
[`coding-dev.md` §4](skills-public/coding-dev.md) for the pattern. Put it in the workspace
|
||||
root's `tests/` so one test covers every member crate, walking `src/` plus each
|
||||
|
||||
+15
-17
@@ -138,19 +138,23 @@ depends on those.
|
||||
|
||||
## 4. Size Limits — Files and Functions
|
||||
|
||||
| Unit | Soft target | Split at | Hard limit |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Source file** | ~400 lines | 600 lines | **1000 lines** |
|
||||
| **Function** | under 50 lines | — | **100 lines** |
|
||||
| **Function parameters** | 3–4 | — | ~5, then pass a struct/object |
|
||||
| **Nesting depth** | 2 | — | 3, then extract |
|
||||
| Unit | Limit |
|
||||
| :--- | :--- |
|
||||
| **Source file** | **1000 lines** max |
|
||||
| **Function** | **200 lines** max |
|
||||
| **Function parameters** | ~5, then pass a struct/object |
|
||||
| **Nesting depth** | 3, then extract |
|
||||
|
||||
These are the limits, not targets to stay well under: there is no earlier "split at" point.
|
||||
A file of 700 lines with one coherent responsibility is fine and is not split for its size.
|
||||
|
||||
### Enforce both mechanically, never by eye
|
||||
|
||||
A limit nobody checks is a suggestion. Pick the **highest** applicable option:
|
||||
|
||||
1. **A linter rule**, where one exists — the function limit is usually covered natively
|
||||
(Clippy's `too_many_lines`, ESLint's `max-lines-per-function`, `golangci-lint`'s `funlen`).
|
||||
(Clippy's `too_many_lines` with `too-many-lines-threshold = 200`, ESLint's
|
||||
`max-lines-per-function`, `golangci-lint`'s `funlen`), configured to 200 lines.
|
||||
A function that trips it wants extracting, not a suppression. Note that some linters only
|
||||
offer *proxies* for length — Ruff's `PLR0915` counts statements, not lines — in which case
|
||||
enable the proxy and put the real limit in a test.
|
||||
@@ -160,24 +164,18 @@ A limit nobody checks is a suggestion. Pick the **highest** applicable option:
|
||||
repository rather than living in one machine's shell script.
|
||||
3. **The build script**, only as a fallback when the project has no test suite to hang it on.
|
||||
|
||||
Whichever you use, it should **fail over the hard limit** and separately *name* anything past
|
||||
the soft split point — so the seam gets found while splitting is still cheap, rather than at
|
||||
the moment the hard limit finally trips.
|
||||
Whichever you use, it should **fail over the limit** and name the offending files.
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn no_source_file_exceeds_limits() {
|
||||
const HARD: usize = 1000;
|
||||
const WARN: usize = 600;
|
||||
const LIMIT: usize = 1000;
|
||||
let mut over = Vec::new();
|
||||
|
||||
for path in source_files() {
|
||||
let lines = std::fs::read_to_string(&path).unwrap().lines().count();
|
||||
if lines > HARD {
|
||||
over.push(format!("{} — {lines} lines (limit {HARD})", path.display()));
|
||||
} else if lines > WARN {
|
||||
// Not a failure: surfaces the seam while splitting is still cheap.
|
||||
eprintln!("note: {} is {lines} lines, approaching the {HARD} limit", path.display());
|
||||
if lines > LIMIT {
|
||||
over.push(format!("{} — {lines} lines (limit {LIMIT})", path.display()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user