The 800-Line Ceiling

This is a post about deleting nothing you can see and building nothing you can click. Stay with me, because it is also a post about why Lux will still feel fast a year from now.

Carving the Core ended with a caveat:

This post is the cleanup. It is also … a status post: the last piece of it is still landing as I write this.

That last piece has landed. Forty-six files had quietly grown past 800 lines (a few of them well past; the framegraph bridge was 3,466), and one by one they became directory modules of small, focused files. But “I went through and fixed it” is the weakest sentence in software. The question that matters to the person running Lux was never whether I could carve forty-six files. It was whether they stay carved.

Why a tidy codebase shows up in your work

Here is the part that connects to a patch you are actually building. The 60fps frame budget (and on good hardware, 120fps) is sacred, and the way it gets protected is by keeping the per-frame hot path free of allocations and stalls. That is far easier to guarantee in a 200-line file that does one thing than in a 3,000-line file that does nine. When a file is small enough to hold in your head, an accidental allocation in the inner loop is obvious. When it is a god-object, that allocation hides for months and then surfaces as a stutter in the middle of your live set.

So this is not housekeeping for its own sake. A codebase that stays legible is a codebase where the framerate stays honest, and where the next feature you have been waiting for ships in days instead of weeks.

Cleanups rot

Every big cleanup has a half-life. You spend a week turning a 2,688-line god-object into fourteen tidy modules, and then over the next three months someone (me, usually) adds “just one more function” to the most convenient file until it is 1,400 lines again and nobody noticed. Entropy does not announce itself. It arrives one helper at a time.

The carve is not finished when the last file is split. It is finished when the codebase cannot slide back. That is a CI gate, not a good intention.

The gate

The 800-line ceiling is now a workspace gate, and it is gloriously dumb:

find app/crates app/plugins app/tools -name '*.rs' \
  | filter out the usual exclusions \
  | count the lines in each \
  | fail loudly if any file is over 800

No syntax tree, no clever per-crate budgets. A find, a line count, a comparison. It runs in seconds, and it sits right next to the legacy-renderer grep gate, the other one-line tripwire whose whole job is to stop a deleted thing from creeping back. Add a 900-line file and the gate fails your PR, and (because the first draft of the message was too terse to help anyone) it now states the budget and the rule: carve it into a directory module before merge.

The first cut of the gate had the usual omission in it. app/tools, home of the bench-summary helper, was not in the search roots, which left the one corner of the workspace most likely to grow a quick-and-dirty 1,000-line script quietly exempt. Added. A gate is only as good as the things it actually looks at, and the first version looked at slightly less than it claimed.

The audit kept its teeth

One quieter follow-on, because it is the kind of thing that bites a month later if you skip it. Lux has a separate guard, the no-hot-path-sync audit, that reads a list of performance-critical files and asserts they contain no per-frame GPU allocations and no blocking syncs. That audit had a list of files. The carve turned several of those files into directories. An audit that opens texture_engine.rs, finds a three-line re-export, and declares victory is not an audit. So it was taught to descend into a carved module and check every submodule inside it. The guard that protects your framerate survived the reorganisation it was supposed to outlive.

What this buys you

Nothing you can see. Not one pixel changed, not one frame got faster today. The entire payoff is in the bad things that now do not happen: no engine file slides back into a thousand-line swamp, no allocation hides in a file too big to read, no feature you are waiting on gets stuck behind a refactor nobody wanted to start.

You will never notice this commit. You will, with luck, notice its absence: the next time something you asked for arrives quickly, and the next time a long live set holds its framerate all the way to the end.

← Back to devlog