Formatting drift is the only kind of drift with no production symptom. A broken
rate limit takes a venue offline, a wrong canonical costs you the index, a
missing env var crashes the process on boot. A file indented with four spaces in
a repo configured for two renders exactly the same page as the file next to it.
So I went looking for how far PubTrivia had drifted, expecting a handful of
files, and got this:
$ npx prettier --list-different . | wc -l
310
For scale, git ls-files lists 335 tracked files with an extension Prettier
handles. So 310 of 335, about 93 per cent, do not match the project's own
config. By extension:
| Extension | Files Prettier would rewrite |
|---|---|
| .tsx | 198 |
| .ts | 90 |
| .json | 17 |
| .mjs | 2 |
| .css | 1 |
| .md | 1 |
| .yaml | 1 |
The config is not exotic
{
"semi": false,
"trailingComma": "es5",
"singleQuote": true,
"tabWidth": 2,
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf",
"plugins": ["prettier-plugin-tailwindcss"]
}
Two spaces, no semicolons, single quotes, 100 columns. Nothing in there is
unusual, and nothing in there is what the repo actually looks like. I counted
indentation the crude way, by comparing lines beginning with exactly two spaces
against lines beginning with exactly four, across every tracked .ts and
.tsx file:
ts/tsx files: 307
predominantly 4 space: 232
predominantly 2 space: 67
tie: 8
So the single most common formatting decision in the codebase is the one the
config rules out.
Nothing in the repo was ever going to catch this
package.json has had both halves of the usual pair from the start:
"format": "prettier --write .",
"format:check": "prettier --check ."
There is no .github directory. No workflow runs format:check, so it is a
command that exists for a human to remember, and I did not remember it once in
four months. A check that only runs when you think of running it is a comment,
not a check.
There is also no .prettierignore, which is how pnpm-lock.yaml ends up on the
list of 310 files Prettier wants to rewrite. A generated lockfile of several hundred kilobytes is not
something I want reformatted by a tool, and its presence on that list is the
clearest sign nobody had ever read the output.
The 25 files that pass are the newest ones
The interesting half of the measurement is the complement. Invert the list and
the 25 compliant files are not scattered at random:
__tests__/billing/checkout-session.test.ts
__tests__/billing/managed-payments.test.ts
__tests__/pricing/display-currency.test.ts
lib/pricing/display-currency-context.tsx
lib/pricing/display-currency.ts
lib/pricing/plans.ts
lib/pricing/server-currency.ts
lib/publisher.ts
lib/seo.ts
lib/site.ts
utils/stripe/checkout.ts
utils/stripe/managed-payments.ts
...
That is one feature. git log --diff-filter=A puts lib/pricing/plans.ts and
utils/stripe/managed-payments.ts in the same commit, three months after
.prettierrc arrived in the initial commit. The pricing and billing work was
written in an editor with format on save, and everything older was not.
The config did not arrive late and find a messy repo. It was there on day one,
and the repo grew away from it, and then one feature grew back toward it.
Why I have not just run it
prettier --write . is one keystroke and a 310 file diff, and that diff has
three costs worth naming before taking them.
The first is git blame. Every line in 310 files gets reattributed to one
mechanical commit. The fix is standard: commit the reformat alone, then add its
hash to .git-blame-ignore-revs so git blame --ignore-revs-file skips it.
Worth doing, but it has to be done in the same sitting, otherwise the ignore
file never appears.
The second is collisions. The WebSocket server is the part of this codebase
under active change, and it contributes 14 files to the list. A whole-repo
reformat lands on top of whatever is half written.
The third is the one people forget: prettier-plugin-tailwindcss is in the
plugin list, and it does not only move whitespace. It sorts the class names
inside className attributes into Tailwind's canonical order. The rendered page
is identical because CSS does not care about attribute order, but the attribute
itself genuinely changes, in 198 .tsx files, in the same commit as the
whitespace. If anything in the app read its own class strings, that would be a
behaviour change hiding inside a formatting commit. Nothing here does, and I
would still rather know that before running the command than after.
What the reader can check, which is nothing
This is the part I like about formatting as a subject. Every page on the
marketing site is built from files on that list of 310. The four free tools are
there as four page files and four components: the
team name generator runs
entirely in the browser, and
the tools index links the scoresheet builder, the
QR code maker and the round planner beside it. The guide at
pub quiz team names is one
more file on the list. Open any of them, read the whole page, view source, and
there is no way to tell from the outside that the source is indented against its
own config.
That is exactly why it drifted for four months, and exactly why the fix has to
be a CI job rather than good intentions. The plan, in order: a .prettierignore
with the lockfile in it, one reformat commit, its hash in
.git-blame-ignore-revs, then format:check wired into a workflow so the number
can never climb back to 310 quietly.
Until that workflow exists, format:check is not a check. It is a wish with a
colon in it.
Top comments (0)