Six thousand lines in an hour, and the monolith took a hundred of them
The paid tier was built in sixty-one minutes. The file that had absorbed every previous feature got 108 lines of wiring and was otherwise left alone.
By the evening of 2026-05-06 this application was one Python file with 9,281 lines in it.
That is not a boast and it is not quite an apology. A single file is the correct architecture for a project with one author and no users, for the same reason a studio apartment is the correct architecture for one person: every wall you build is a wall you have to walk around. For ninety-seven days, everything went in backend.py, and everything was findable, because everything was in the same place.
Then, between 17:55 and 18:56 on 2026-05-06, the product grew a commercial layer. Six numbered phases, sixty-one minutes, several thousand lines of implementation. The main file gained 108 of them, and more than half of that was one feature that had nothing to do with the paid tier.
Phase 0 is two documents
The first commit of the six contains no code at all. It is an architecture document of 449 lines and a build plan of 417, committed at 17:55, and nothing else.
Eight hundred and sixty-six lines of prose, and then fifty-eight minutes of building. I have written before about why this works, but the sequence here is unusually stark: the plan took longer to write than the implementation took to type, and that is the trade. By the time Phase 1 started there were no decisions left, so the remaining hour was transcription.
Phase 1 created a package. Authentication for API tokens, a webhook delivery system, a migration helper, and mobile detection. Four new modules, 874 lines between them. The monolith gained twenty-six lines, all of them wiring.
That is the moment this entry is really about. For ninety-seven days, "add a feature" and "make backend.py bigger" were the same operation. On this evening they stopped being the same operation, and it happened not because anyone announced a refactor but because the new thing was shaped differently from the old things.
The exception proves it. Phase 2 was a mobile-detection redirect, and it put 54 lines straight into the monolith, which is more than the entire paid-tier foundation did. It is the one piece of that hour that was not a package, and it is also the one piece that had to be undone four days later.
Phase 4 shipped the public REST API: 416 lines of endpoints, 204 of admin routes, a 309-line machine-readable specification, and three templates. The main file gained seven lines.
Phase 5 shipped the enrichment waterfall, which is the feature where you ask for a person's email address and the system tries vendors in sequence until one of them knows. A routing module, a routes module, a credits module, a shared adapter base, and then five separate vendor adapters written in a single commit. Seventy-six to ninety-nine lines each. The main file gained eleven lines.
Forty minutes later, bulk enrichment: another 523-line module for running that waterfall across a whole list at once.
Why the waterfall is the interesting one
Five vendor adapters in one commit sounds like padding, and it is worth explaining why it is not.
The reason a waterfall exists at all is that no data vendor is good. Each one knows a different, partially overlapping, unpredictably stale subset of the world, and each one charges you whether or not it knew the answer. A single-vendor integration is a bet that one company happens to have the contacts you need, which is a bet you lose in a way that costs money and looks like your own product failing.
So the correct shape is a chain: ask the cheap one, and if it does not know, ask the next, and stop the moment somebody answers. The value is not in any adapter. It is in the ordering, the stopping, and the fact that you only pay for the call that worked.
Which is why they had to be written together. An adapter interface designed against one vendor is an adapter interface designed against one vendor's idiosyncrasies. Writing five of them in one sitting is the only way to find out which parts are genuinely shared and which parts you were about to enshrine as universal because the first API you read happened to work that way.
It was also the only phase of the six that did not land cleanly. It needed a follow-up commit, and then another one. That is not a coincidence either: it is the only phase whose correctness depended on five external systems, none of which had read the plan.
There is one small piece of this evening that got quietly undone. Phase 2's mobile redirect sent phone traffic to a dedicated page. Four days later a commit went in whose subject reads, in part, "let /agents through as marketing," followed by a parenthetical noting that it had been redirecting to the mobile gate. The parenthetical is the confession. A gate written to catch app traffic on a phone had been catching marketing traffic on a phone, which meant a prospective customer on a train, tapping a link to read about the product, got bounced to a page telling them to come back on a desktop.
That is the characteristic failure mode of any rule that runs before you know what the request is for. The gate was correct about the device and wrong about the intent, and there is no amount of device detection that fixes that.
Sixty-one minutes is a strange amount of time to have built a billing surface, a public API, a webhook system and a five-vendor data pipeline. It reads like recklessness. What it actually was is the compounding return on an hour of writing prose first, plus the fact that none of it had to fit inside a file that already held nine thousand lines of earlier decisions.
The monolith is still there, incidentally. It is bigger now than it was that night. But the ratio changed permanently on 2026-05-06: from that evening onward, the interesting new things in this codebase mostly happen somewhere else, and backend.py gets a few lines of wiring and is left alone.
That is what growing up looks like for a codebase. Not a rewrite. A shift in where the next thing goes.
— DealArena Team