← back

Getting a Codebase Ready for Its First Outside Review

#ai-product-management#code-quality#data-governance#solo-founder#security-hygiene

A friend audits the code next week, so I spent the weekend reading it as an outsider would and removing what I could not defend.

A friend who has spent a career as an engineer is going to read the Duskglow code next week. I asked for it. And the moment the answer was yes, I started seeing the repository the way a stranger would, which is a different thing from seeing it the way I do after six months inside it. This weekend went to that reading, and to the settings and documents around the code that an audit would reach next.

What is unused is not neutral

The user-interface folder held 49 building blocks from the generator that scaffolded the app in April. Forty of them were never imported anywhere. Not once. They came with 33 packages that only they depended on, and when both went, 72 packages left the lockfile.

I had known those files were there. I had not counted them, and the count is what changed my mind about leaving them. A reader who opens that folder sees 49 things and has to work out which nine matter. Every unused package is also a thing I have to keep patched, and a thing a dependency scanner can keep flagging, for no return. Eleven audit findings went to zero across the weekend, and the last two went with a package I removed rather than upgraded.

Even the package still carried the name the generator gave it. Small, and the kind of detail that tells a reviewer the owner has not looked closely. A 4.1 MB folder of design exports from June, read by no code, was still in the tree too. Out.

Lint debt tells you where the ritual starts

Lint errors went from 23 to 13. The 13 that remain are all in server code that the browser-side linter cannot type, and the fix for those goes out with the next deploy of each function rather than as a standalone change, because every server deploy here is a paste I do by hand with a test suite behind it.

I like that split more than a zero. It says which parts of the codebase I can change cheaply and which parts carry a ritual, and a reviewer can see the line.

Three releases went out, each through the same gate, the type check and the build and the test suite and the browser gate on three engines. Only the first touched anything a person sees, so only that one got the phone check. The other two were a dependency removal and a folder move, and the production read afterwards stood in for a smoke.

The README is for the person who was not there

I wrote a README for a first-time reader. Four things: how the code is laid out, the data boundary in one paragraph, how to run and check it, and how a change ships. That last section is the one I expect my friend to read twice, because the shipping rules here are stricter than the size of the project suggests, and without the reason beside them they look like ceremony.

The instruction file I keep for my AI collaborator got the same treatment. It had drifted. It described a lint count from two weeks ago and a beta window that had since moved by a week. Stale instructions are worse than none, because the reader trusts them.

Settings no one reads between checks

An audit reaches past the code. The consoles around it hold settings that were right when they were set and have not been read since. I keep a list of those, with the intended value beside each and the date it was last seen, and six of them were past their date.

I read all six from screenshots this time, against the intended values, after having the current click paths checked against each vendor's own docs so the steps would carry today's labels. Five matched. One did not, quite. A list of addresses the sign-in flow may return to still carried two addresses the hosting platform hands every project by default. Nothing used them. Both had been superseded weeks ago when I moved phone testing to a dedicated subdomain.

Unused today, and that is the trap. The platform hands out those addresses first come, first served, and it makes no promise about them once a project is renamed or deleted, so a stale entry on that list could one day let a stranger's host receive a sign-in. I removed both. The reason I had kept them (convenience during early testing) had expired, and the entry had outlived its reason, which is the failure I keep finding in my lists of exceptions.

Read the law, not the summary of the law

The incident response plan cites California's breach-notice law, and the register I keep of outside facts said the citation was due for a fresh read from the statute's text. The state's legislative site refuses automated readers, so I read an archived copy of the statute's own text from three weeks ago and checked the threshold against the Attorney General's own page.

The plan's deadlines were right. Thirty calendar days to notify the people affected, fifteen calendar days to send the Attorney General a sample notice once more than 500 California residents have been notified. The trigger was not. The plan said "500 or more residents affected." The statute says "more than 500 residents notified." Different threshold, different trigger. Without the scheduled re-read, it could easily have gone unnoticed until a breach, which is exactly when nobody should be discovering it.

The upgrade I had been putting off

A minor database upgrade was waiting. I had deferred it for a month because the beta's start date kept moving and I did not want downtime near a first night.

With the beta pushed to November, the week before it was the right window. The database is 27 MB, so the upgrade, which copies everything to a fresh instance, took minutes. The part worth writing down is what I checked afterwards, because the vendor's guide says the job scheduler is dropped and recreated during the upgrade, and the evening reminders depend on it. So the job list was the first thing I read back. Then the extension versions, the access policies, the triggers, the row counts against the morning's read, the schema fingerprint, and the server functions' versions. Then a sign-in and one message to Luna from my phone. All of it matched.

None of that is clever. It is the same list I read at every session open, run one more time against a database that had just been rebuilt. The point of having the list is that on a day like this the only things left to decide are what to read first and what the change adds.

Mental Models

What you cannot defend, remove. A reviewer asks whether you knew it was there and how much of it there is. Count it, then decide.

A convenience needs an expiry. An exception to a default usually had a good reason on the day it was made. Record the reason and what ends it, and re-read the entry when that happens or by a set date, whichever comes first, because the entry will not remind you itself.

Read the primary text when a clock depends on it. A paraphrase of a statute can be a couple of words off and still look right. The words matter most in the sentence that starts a deadline.

Keep a standing post-change checklist, and let the change decide what you read first and what it adds. The read I run after an upgrade is the read I run at every session open. The value of the list is that it already exists when I am least inclined to write one.