The Dotfile That Almost Shipped
I was about to push a 4,700-line app to GitHub. I looked at the file list one more time and saw `.env`.
I was going through the diff one more time before I pushed. Muscle memory. I don't know why I bothered — the repo was brand new, I knew what was in it.
I saw .env in the file list.
My stomach dropped.
The Two-Line File
The .gitignore a Next.js scaffold gives you does not, by default, ignore .env. Or /data/, which is where my SQLite database was about to live.
Both had been tracked from the very first commit. I hadn't touched .gitignore because I assumed the scaffold had my back. It didn't.
Nothing had been pushed. Nothing ever got pushed. The catch happened five minutes before I ran the command that would have uploaded my JWT secret to a public repo for anyone who bothered to look.
I added two lines to .gitignore, rewrote the history so the tracked files were actually gone, and sat for a minute before I did anything else.
What Drift Is
The thing I was about to push is a small personal tool. You pick some dimensions of your life you care about — for me it's health, creativity, a few others — you check in once a day and score each one, and over time the app plots where you're drifting. Not in a motivational way. It just plots the line.
I built it because I'd been sliding on a few things for longer than I'd realized and I wanted something that would tell me before I noticed on my own. That's the whole thesis.
Fast to build, because I knew exactly what I wanted. One long Thursday. Initial commit from Create Next App in the morning, working life-trajectory tracker by evening. 4,700 lines. One very good cup of coffee and a project I'd been thinking about for a month.
Twenty-Two Findings
After I wrote it, Codex reviewed it.
I have a setup where Codex — a different model than the one doing the writing — gets the diff and attacks it. Correctness, security, performance. It doesn't suggest improvements I haven't asked for. It just finds problems.
On a 4,700-line diff it finds a lot.
The first round's list was long enough that I had to scroll. The second round found more. Twenty-two issues across the two passes. I fixed all of them before I shipped.
A few were the kind of thing that hurts because they're so obvious in retrospect. Every endpoint accepted whatever you sent it — any number, any date, any field. The JWT secret had a fallback for local dev that was also accepted in production. The CSV export didn't guard against formula injection, so a dimension named =HYPERLINK(...) would execute in Excel on open.
And .gitignore. Twenty-second finding. Five minutes before I'd have pushed.
All of these were the kind of bug I'd have sworn I would never ship, and had already written.
The Vegetables
The day before Drift, I did something almost completely boring.
I took WFSRA — the billing app I maintain for a small neighborhood association — and I set up a migrations pipeline and a test suite. Nothing a user would see. No button, no page, no feature. Just the plumbing I'd been avoiding for months because I knew it wouldn't feel like progress.
It felt like forty minutes of nothing.
And then on Thursday, the reason I could move as fast as I did on Drift was that I'd spent Wednesday on vegetables. Migration discipline means I'm not hand-writing schema changes in a commit titled chore. Tests means I'm not afraid to refactor. CI means the thing I wrote works on a machine that isn't mine.
I do not enjoy those days. I have never once enjoyed them. The weeks I skip them are the weeks I regret something later.
My Website Has Been Lying to Me
On Friday I opened this site to add a new section. The version number at the top said v0.1.0.
The site is on v0.4.0.
The version display is a small component in the header. It had a hardcoded string. I shipped v0.2.0 and it still said v0.1.0. Shipped v0.3.0, still v0.1.0. Today I'm shipping v0.4.0 — the one with the new section — and I finally noticed the number didn't match.
I fixed it. Three releases of a lie, corrected. What I keep thinking about is the part where I'd walked past it every day. I'd looked at it. I hadn't read it. Same kind of blindness as .gitignore getting tracked from the initial commit. You know the thing is there, you know it's supposed to do its job, you stop checking because you trust it.
The site had been telling me. I'd stopped reading what it said.
Mission Control, Open on the Table
Mission Control — the thing I use to run all of this — has been in the middle of an operation all week. I opened it up to split a monolithic chat UI into real pieces and haven't closed it back up yet. Heart surgery in progress. I'll close it up next week.
I caught .gitignore five minutes before I'd have leaked secrets to the internet. I missed the version display on my own site for three releases in a row. Those feel like opposite mistakes. They're the same mistake.
I'd been trusting the scaffold. The scaffold lies sometimes. I need to read what it says.
Drift is live. WFSRA has tests. This site finally tells me what version it is. Mission Control gets closed up next week.