I have been writing about this app for six weeks: its CSS, its queue, its tests, its contrast ratios, its spelling. It never occurred to me to measure the repository itself. So, before anything else, the shape of it:
commits 88 authors 1 merge commits 0 tags 0 branches 1 .github directory none first commit 2026-10-01 Initial commit from Create Next App last commit 2026-11-09 Raise YouTube daily quota and search allowance Forty days. No pull requests, no CI, nothing that runs on a push. pnpm typecheck and 611 tests in 4.2 seconds run on my machine before I push, and Vercel builds what lands on main. I am not recommending it. I am saying that is what the history is, and the interesting part is what the history gets wrong about the work.
The busiest day holds 22 commits and the last twenty days hold two
2026-10-01 2 2026-10-02 9 2026-10-03 22 2026-10-04 4 2026-10-05 10 2026-10-06 4 2026-10-07 8 2026-10-08 2 2026-10-09 5 2026-10-10 3 2026-10-12 3 2026-10-14 1 2026-10-15 3 2026-10-16 3 2026-10-17 2 2026-10-18 1 2026-10-19 2 2026-10-21 2 2026-10-28 1 2026-11-09 1 Twenty-two commits on 3 October is the day the public side of the site went up: the guides, the niche pages, the comparison pages, the blog. Sixty-three content pages in a day, which is less impressive than it sounds, because a content page in this codebase is a typed object in a registry rather than a route.
The tail is the part worth sitting with. Two commits in the last twenty days, and both are small: one rewording commit and one that raised a quota by changing thirteen lines.
I did not stop working on this. What I did in those twenty days was measure it: contrast on every text run, spelling on every page, the HTML validity of all 76 pages, the dependency failure matrix, the accessibility of the tables. That work produced findings, and findings are not commits. They are entries on a list and paragraphs in posts, and in several cases they produced a decision not to change anything.
A commit graph is a record of typing. If you read mine as a record of attention, you would conclude the project was abandoned on 21 October, which is the opposite of true: the last three weeks are the only weeks I have properly understood what is in it.
The median commit touches 11 files
files changed, total 2,503 insertions 214,547 deletions 21,080 files per commit min 1 median 11 max 283 The five largest:
283 Automate creator outreach with Stripe billing and admin features 174 Build Nakodo: branding, billing, and core features 162 Refactor limits system and action handling 141 Add business campaigns with Trigger.dev places integration 115 Build YouTube creator discovery app These are not commits, they are days. Nobody reviews them, including me, and that is the honest reason they are allowed to be this size: a commit boundary is a unit of review, and with no reviewer the only pressure left is the one I apply to myself out of habit.
The cost is specific. git bisect over a history like this does not find a bug, it finds a day: the answer to "which change broke this" is a commit that changed 141 files, which is a worse answer than reading the code, and the only reason it has not cost me real time yet is that nothing has broken badly enough to need bisecting.
The fix is not "squash less" or "commit more", it is a rule I can actually follow: a commit should change one noun. "Add business campaigns" is four nouns and a provider integration.
Forty-five per cent of every line ever added is machine output
drizzle/meta 95,616 src/lib 34,564 src/app 33,881 src/content 14,030 pnpm-lock.yaml 13,136 src/components 7,328 reels/src 4,182 docs/brand.md 1,612 src/db 1,534 drizzle/meta is the ORM's snapshot folder: one JSON file per migration, holding the whole schema as it stood, regenerated every time I generate a migration. It is 95,616 of the 214,547 insertions in this repository's entire history. More than src/lib and src/app put together.
I have written about the folder being 2.4 MB against 36 KB of actual SQL, but that measured the state. This measures the flow, and the flow is worse, because every migration rewrites the snapshot rather than appending to it. Add the lockfile and the number of lines in this repository that a human typed is about 91,000, for an application that is 72,683 lines long today.
That gap, 91,000 typed against 72,683 standing, is the clearest number I have for how much of this app was written and then unwritten. Set against only 21,080 deletions, it says most of the churn happened inside files rather than by removing them.
Forty-five different first words, and not one prefix
Add 27, Show 6, Remove 4, Stop 2, Move 2, Make 2, Hide 2, Fix 2, Build 2, Automate 2, Allow 2, then 36 verbs used exactly once: Use, Update, Track, Tidy, Standardize, Skip, Send, Search, Reuse, Retire, Refactor, Read, Rate, Raise, Price, Pause, Only, Mark, Keep, Introduce, Implement, Hand-maintain, Generate, Gate, Expand, Drop, Discard, Disable, Detect, Consolidate, Centre, Call, Bring, Alternate conventional commit prefixes (feat:, fix:, chore:) 0 issue or ticket references 0 average subject length 54 characters longest 83 Pause Free campaigns at the monthly outreach limit, and give paid plans a daily one No feat:, no scope, no ticket. What came out instead is a product changelog by accident. Read down the list and you get the product's history in plain English: "Show Free the best 20 creators and blur the rest, instead of stopping the search". "Price in visitor's currency and turn off Adaptive Pricing", which is the commit behind what you see on the pricing page in your own currency. "Keep conversations in Nakodo after a yes instead of introducing by email", which rewrote one sentence on twenty-three pages including how it works.
Two things in that list I would not have predicted. Hand-maintain is a verb I apparently invented for a commit. And Centre, with the British spelling, which is the only commit subject where the spelling of the codebase leaks into the spelling of its history.
The pattern that makes these messages useful is not the mood or the length, it is that almost every one names a behaviour and several name the behaviour they replace. "Instead of" appears in three subjects: spam discarded instead of forwarded, conversations kept instead of introduced, Free blurred instead of stopped. A message that says what the software now does and what it stopped doing is one I can still read six weeks later. fix: counter bug would not have survived the month.
The file everything passes through
39 src/db/schema.ts 33 src/app/(app)/campaigns/[id]/outreach/page.tsx 32 src/app/(app)/campaigns/actions.ts 25 src/lib/queries.ts 25 src/app/(app)/campaigns/[id]/results-view.tsx 24 drizzle/meta/_journal.json 22 src/lib/outreach/send.ts 22 src/lib/outreach/reply.ts 21 src/app/(app)/campaigns/[id]/creators/[channelId]/page.tsx 21 README.md 20 src/lib/plans.ts The schema is in 39 of 88 commits. Forty-four per cent of everything I have ever done to this codebase involved changing the shape of the data, which is either an indictment of my up-front design or just what building a product is. I lean to the second: nearly every one of those 39 is a column added because the product learned something, and 318 columns with 147 nullable is the shape that leaves.
The opposite end is more cheering:
tracked files under src/ 588 files that have only ever appeared in one commit 197 A third of the source files have never been edited since the commit that introduced them. That is not dead code, it is mostly the content registry and the illustrations, where a file is one page or one drawing and is finished when it is written.
You can read the product's mind in the deletions
Forty-five files have been deleted over the life of the repository, and they cluster:
12 files Build Nakodo: branding, billing, and core features 7 files Automate creator outreach with Stripe billing and admin features 6 files Consolidate onboarding into campaign setup review page 4 files Automate creator outreach to best fits, drop shortlist The twelve in the first one are Create Next App's starter: public/next.svg, public/vercel.svg, window.svg, globe.svg, file.svg and friends, which survived exactly one day.
The four in the last one are a feature that existed and does not: shortlist.tsx, contact-button.tsx, contacts.tsx, drafts.tsx. The app used to find creators, show you a shortlist, and let you press a button to contact one. On 2 October it became an app that contacts them for you, and those four files are the difference. No design document records that pivot as clearly as the deletion does.
The six in the third are 460 lines of onboarding that became a 44-line redirect. Three deletion clusters, three product decisions, and all three are legible from git log --diff-filter=D alone, which is now the first thing I would run on a codebase somebody handed me.
What I would keep
The messages. Behaviour-first, imperative, no prefix, and "instead of" when something is being replaced. Six weeks on they are the only documentation of why the product is shaped like this, and they cost nothing to write.
Not having CI, for exactly this long. Forty days, one person, tests that finish in four seconds. The cost of a push-triggered pipeline was not zero and the benefit over running them locally was. The moment a second person touches this, that answer flips, and it flips before they land anything.
What I would change
One noun per commit. Not for history's sake, for bisect's.
Count the generated lines separately. Any measurement of this repository that includes drizzle/meta is reporting the ORM's diary, not my work, and I have quoted insertion counts in these posts before without checking which half I was quoting.
Stop treating the commit graph as the record. Three weeks of audits produced two commits and a list of findings, and the findings only exist because I wrote them down somewhere that was not git. If the work you are proudest of leaves no trace in the history, the history is not the thing to be proud of.