Audience: All contributors
Execution context: N/A
Maturity: Stable doctrine
AI-assisted development has a structural bias toward complexity: adding features "just in case," creating helpers for single uses, over-engineering simple problems, building systems without wiring them in. In an OS codebase — where the wrong abstraction lives forever in the kernel ABI — this bias is more dangerous than in application code.
The goal is sanity, not sacrifice — keep code clean without stripping legitimate verbosity or readability.
These are guidelines for when to pause and think, not absolute
rules. A clean 450-line .cpp is fine; a cryptic 200-line .cpp
is not.
| Thing | Threshold | What to do |
|---|---|---|
.cpp / .c / .rs file size |
~500 lines | Split if doing multiple jobs; leave if one coherent unit |
.h / .hpp file size |
~300 lines | Split if unrelated types; data-heavy headers are fine |
| Public methods per class | ~15 | Ask: "Does each method earn its place?" |
| Function length | ~60 lines | Split if nested branching; clear linear flow is fine |
| Syscall handlers per file | 1 subsystem per file | Consolidate before adding more |
| Parallel subsystems doing the same thing | 0 | Remove the duplicate |
Never sacrifice readability to hit a line count. Keep:
- Comments that explain "why"
- Descriptive variable names (
pageTableEntryMask>ptm) - Vertical whitespace between logical sections
- Braces for non-trivial loop bodies
- One statement per line
The question is always: "Does this make sense to someone reading it for the first time, at 2am, during a triple-fault?"
- Does this already exist? Search before writing — especially for low-level primitives (spinlocks, allocators, list helpers).
- Will this be called? If you can't name the caller, don't write it.
- Can existing code do this with a small change? Prefer editing over adding.
- Is this a one-time use? Inline it — no helper function, no new class.
- Am I future-proofing? Stop. Write only what is needed today.
- Adding a new subsystem? Ask if an existing one can be extended instead.
- Adding a new syscall? Syscall numbers are an ABI. Once published, they are forever. Be sure.
- Is the code dead? Delete it. Don't comment it out — git history exists.
- Is a system built but not wired in? Either wire it in or delete it.
- Is this running in kernel or user space? Be explicit. Kernel
code has no
malloc, noprintf, no exceptions unless the project explicitly supports them.
A system that exists but is never initialized, called, or connected is worse than not existing. In kernel space, dead code is not merely wasteful — it rots silently until the day a refactor accidentally re-enables it and triple-faults the box.
- Every driver must be probed. If
probe()exists, the bus enumerator must call it for matching devices. - Every syscall handler must be in the dispatch table. A handler that compiles but isn't dispatched is dead code.
- Every initcall must run. If a subsystem has an
init(), it must be on a known init list with a stated ordering. - Every sink must have a source. If a system receives data, something must be sending it.
If you discover a subsystem that is built but not wired in: either wire it in immediately, or delete it.
Specific failure modes that recur when an LLM is the author:
- Adding a "future-proof" abstraction the next slice will need to delete
- Building parallel subsystems instead of extending the one that exists
- Wrapping a one-line operation in a helper function with a five-line docstring
- Adding error handling for cases the function's preconditions rule out
- Inventing a feature flag for a binary decision the user already made
- Re-introducing a deleted utility because a new caller wanted it back rather than rebuilding the pattern that originally justified the deletion
- Building a "config / preferences" struct around a single tunable whose call site already names the value
Recognise the pattern; refuse the urge.