373,000 lines with an agent: the parts that actually hold
Generating code stopped being the hard part a while ago. Everything I do differently now is about the other half: making it cheap to find out whether the code is wrong.
TalosOps is about 373,000 lines of TypeScript with 424 database migrations and 7,822 tests behind it. I am the only engineer on it. I did not type most of it.
That sentence gets read two ways, and both are wrong. It is not "AI wrote a platform" and it is not "the code is slop that happens to compile". What actually happened is more boring and more useful: generation stopped being the bottleneck, so every constraint moved to verification, and the way I build had to be reshaped around that.
The failure mode is plausibility, not errors
An agent almost never gives you code that fails to compile. It gives you code that compiles, reads well, matches the surrounding style, and is subtly wrong — a check that should have been there, a permission one notch too wide, a verification that looks like one and is not.
Reviewing that at the speed it arrives is not possible by reading alone. Attention is the scarce resource, and prose is the most expensive place to spend it.
So the question stops being "how do I get better code out" and becomes "how do I make being wrong loud?"
Put the invariant where code cannot forget it
The highest-leverage decision in every one of my products is that the rules that matter most live in the database and in typed boundaries, not in application code that has to remember them.
Isolation between customers is enforced by the database, so a forgotten filter is a broken feature — the cheapest possible failure — instead of a data leak nobody notices for six months. Visibility is one derived value every read path shares, so there is no second rule to get wrong. Consequences come from written policy, so a prompt edit can never change a penalty.
None of these are AI-specific ideas. They are ordinary defensive design. What changed is the payoff: when code arrives ten times faster, a constraint that catches a whole class of mistake gets more valuable by roughly the same factor.
Migrations are the memory
Hundreds of migrations is not a mess, it is a record. Each one is a small, named, reviewable statement of what changed and when. A schema I can read is a schema an agent can read, and the schema is the one artifact that never drifts from reality.
They also record mistakes honestly. When a boundary is added and later tables are not wired into it, the migrations that close the gap are the honest record of getting it wrong once — and anyone reading the directory can see both the design and the day it slipped.
In nat3alm the tests run the real migration files against an embedded Postgres. No fixture database, no mocked client, no separate test schema that gets out of date. If a migration is wrong, the suite is red immediately, on the machine where it was written.
The docs are for the agent too
Every one of my repos has a long document explaining what the thing is, which parts exist, and which decisions are load-bearing — one of them written in Arabic prose with the technical names left in English, exactly as they appear in the code.
I wrote it for a person. It gets read by an agent every session. Context is not something you paste at the start of a conversation — it is a build artifact you maintain, and the version in the repo is the version that is true. The corollary: a stale document is now a bug with a blast radius, because a wrong sentence will be believed, confidently, and built on.
Ask for a decision before a diff
The workflow that failed for a long time: describe a feature, get an implementation, review it, discover the design was wrong three files back, throw it away.
The one that works: get the decision on the table first. What are the two ways to do this? What breaks in each? Which one costs more later? Then the diff — and by then the diff is small, because most of the argument already happened in a paragraph instead of in six hundred lines.
Every "decision" section in my case studies exists because that conversation happened before the code did. If I cannot explain why the alternative is worse, I do not understand the code in my own repository, no matter who typed it.
Measure instead of arguing
The best example is search. Keyword search or embeddings is exactly the sort of question that can absorb a week of opinion. So it was measured, against the real documents and the real questions customers ask in the scripts they actually use. Keyword search failed them; semantic search found the right passage almost every time. An afternoon ended the discussion permanently. An agent will happily argue either side for as long as you let it. A number will not.
What I still do by hand
- Every schema change. I read all of them.
- Anything touching money. Payment integrations are written slowly, and they refuse everything until they can verify it. That refusal is the feature.
- Deletes and grants. Anything that can destroy data or widen access.
- The prose. Including this.
The honest summary
The productivity story is real, and it is not "ten times faster at everything". It is: generation went to nearly free, review stayed exactly as expensive as it always was, and the craft moved into designing systems where being wrong is obvious, cheap and early.
That is the thing I am going to spend the next while showing rather than describing — real sessions on these codebases, including the ones where the agent confidently ships something broken and I have to catch it. Those are the useful ones.
Also
If a ranking term is unbounded, your weights are decoration
A feed score with a small weight on engagement can still be almost entirely engagement. Bounding every term is what makes the numbers in the formula mean anything at all.
Publish first, review after
Every moderation design starts with the same question, and almost everyone answers it backwards: does review let content up, or take it down?