Repository navigation
chore(lint): no single-character names, enforced and swept repo-wide - #129
Merged
Merged
Conversation
jayleaton
force-pushed
the
chore/naming-lint
branch
from
October 23, 2026 11:26
0f812a7 to
6be62a1
Compare
…lint-layering; renamer for the sweep Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jayleaton
force-pushed
the
chore/naming-strict
branch
from
October 23, 2026 11:36
e484a3c to
ad1e907
Compare
jayleaton
marked this pull request as ready for review
October 23, 2026 11:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebuilt on main acb5cd1 after #117 merged (3e420ea): the renamer re-ran over everything, including the new storage, imaging and key-rotation code. That code (
packages/storage,imaging) already used words, so nothing there needed renaming.Problem
JJ's rule, final: "No single character names. It makes it far easier for a human to read and far easier for an agent to look up code that is well named." The template had 2,424 single-character declarations (
func (s *Service),w http.ResponseWriter,t *testing.T,for i := range) and nothing stopped new ones.Fix
Lint:
scripts/namelint, run bymake lint,make check-fastand CI (root module anddeploy/). It's a ~60-line go/analysis checker: every identifier a package declares (types.Info.Defs) with a one-character name fails. That covers receivers, params, named results, locals, loop and closure variables, type-switch variables, type params, fields, labels and consts, in tests too. The message names the fix:single-character name "w": use a descriptive name: writer, request, test, index, service, caller (AGENTS.md "Taste"). Generated files (sqlc,mocks/) are skipped.*_templ.gois checked because templ copies the.templsource's Go verbatim (its own names aretempl_7745c5c3_*), and the message says to fix it in the.templfile. There is no//nolintescape.Why not a golangci-lint linter: I checked
varnamelen(v0.8.0 in golangci v2.15.0). Itsmax-distancecan't go below 1 (golangci drops 0), sofor _, u := range users { use(u) }passes. It also hard-codest *testing.T,b *testing.B,m *testing.Mand similar as conventional, with no way to turn that off.forbidigoskips declarations and never sees a receiver used ass.field. No other linter checks name length. A golangci module plugin would mean building a custom golangci binary in CI.Probes:
make lint-layeringplants the real past examples and requires a report for each, then requires the fixed form to pass:Sweep: every one of the 2,424 declarations, and all their uses, was renamed with a type-aware renamer (
scripts/namelint/rename, go/packages + go/types; gopls isn't installed here, and 2,400 singlegopls renamecalls with shifting offsets would be slow and fragile). It:writer/request/test/ctxfor the stdlib types;servicefor a*Servicereceiver, consistent per type;userwhen ranging overusers;left/rightfor comparator pairs;bodyforb := jsonBody(...);testCasefor table rows;.templsources through templ's source map (gofmt-aware: maps by token index), thenmake generateregenerates templ output and mocks.The names it could not derive safely are hand-picked in 93 override lines in
scripts/namelint/rename/overrides*.txt(for exampleSecretsreceivers →accessors,i18n.T→i18n.Translate, JWK fieldsN/E/X/Y→Modulus/Exponent/CurveX/CurveYwith theirjsontags unchanged, and receivers whose type name was taken).AGENTS.md "Taste": the "advice only" naming line is replaced by the strict rule, and the line says lint enforces it.
Behaviour unchanged (real output)
make check-fast(full run):check-fast: ok in 15.6s. golangci-lint 0 issues, namelint 0 in the root module anddeploy/, vet clean.testbudget: 0 differing instructions.railway-setup: only NOP padding, plus 8 lines where objdump disassembles embedded data.server,admin,backup,preview: remaining differences are renamed symbols only:i18n.T→Translate, renamed struct fields inside generic shape andtype:.eqnames, and templ'sCol:error metadata, which moves when a name changes length./healthz, sign-up, authenticatedGET /users/me, anonymous 401, a validation error, the image route, the UI. With ids, tokens and timestamps masked, the responses are identical.make lint-layering: 112 probes ok.ci.ymlrun on this branch.Possible visible effect: a
%+vprint of a struct with a renamed unexported field shows the new field name. JSON output is unchanged: exported renames keep their tags, and unexported fields are never encoded.Reproducing the sweep after a rebase
New code merged after this sweep gets the same treatment. Positions in the overrides files go stale after a rebase. A stale override is harmless: its position no longer matches, and the renamer derives the name or prints
SKIPwith the reason. The renamer and its overrides are sweep tooling, and can be deleted in a follow-up once every branch has merged.Merge risk
High churn (173 files), low behavioural risk: all renames are type-checked and the binaries match as described above. Every open branch that adds a single-character name fails lint after this merges, so it merges last, right after #117.
🤖 Generated with Claude Code