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:
BluscreamandClaude Opus 5.5 committed 2026-10-18 21:52:48 +02:00
1 parent e385e01993
commit 44c2d1502c
4 files changed
+26 -25

No files matched your search

+15 -17
View File
@@ -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()));
}
}