I rebuilt a month of old agent work in about a day, and I'm still deciding how to feel about it
This spring I built an AI website generator on Replit. It's called yourveryownweb.site, and the pitch is simple enough to fit in the domain name. You describe a site, a model writes it, and instead of hosting it inside my walled garden the app commits the code to a GitHub repository in your account and turns on GitHub Pages. You leave with a repo you own, a live URL, and no dependency on me continuing to exist.
Building it the first time took about half a week (maybe 2-3 sessions of 1-2 hours prompting an app builder agent, and 1-2 hours of research and kicking tires). Last night I finished rebuilding the whole thing from scratch with Claude Code running Fable 5, and the rebuild took less than a day.
I want to be careful about what that sentence means, because the easy reading is wrong. The easy reading is "the new model is that much better than what came before." Some of that is true. But the honest accounting has at least three entries, and the model is only one of them.
The rebuild had a spec, and the spec was the app
The first build was slow because I was designing while building. What should a credit cost? What happens when a generation fails mid-stream? Should publishing to a taken repo name auto-suffix or error? Every one of those questions cost a session on Replit, and some of them cost three, because I'd answer them wrong first.
The second time, all those decisions already existed as a running application. I copied the old codebase into an old/ folder in the new repo and pointed Claude at it, not as code to port but as behavior to match. The most useful document in the whole project turned out to be a parity audit, a table of every feature in the old app with a status against the rebuild: matched, matched-but-better, deferred, or deliberately dropped. When you can hold the entire product in one table, a day is a plausible amount of time to rebuild it. When you can't, it isn't. Most of my three months went into producing that table, even though I didn't know that's what I was doing.
What I changed, because this time I got to choose the ground
The Replit version was shaped like a Replit app: a pnpm monorepo, Express 5, Postgres with Drizzle, Zod validation, OpenAPI codegen through Orval, esbuild bundling, and Stripe wired through Replit's connector, which fetches credentials per-request and syncs Stripe state into your database through its own sidecar package. None of that is bad. All of it is what you reach for when the platform is making the infrastructure decisions and you're renting everything by the request.
Knowing I was leaving, I rebuilt on the stack I'd have picked from nothing:
- PocketBase instead of Postgres-plus-framework. The whole backend is now one Go binary. PocketBase brings SQLite, migrations, auth, and an admin UI, and my application code lives in Go packages hooked into it: the credit ledger, the generation worker, the GitHub Pages publisher, the Stripe integration. The old app needed a database server, an ORM, a codegen step, and a bundler to do what one process does now.
- A small VM instead of a platform. It runs on a single shared-CPU VM machine with 512MB of memory. The SQLite file lives on a volume, and Litestream replicates it to Cloudflare R2 every ten seconds, with 72 hours of retention. That's the entire disaster story: if the machine dies, the database is sitting in R2, at most ten seconds stale.
- Cloudflare for the edges. R2 holds user media (uploaded directly from the browser with a two-phase intent/commit handshake, so half-finished uploads get reaped and refunded) and the database replicas. DNS and the domain sit there too.
- The same GitHub OAuth app. This was the one piece of continuity I refused to break. Users authorized yourveryownweb.site once; the rebuild uses the identical OAuth application, so nobody re-consents and every existing repo relationship survives. PocketBase's native GitHub provider handles login, and a hook captures the access token for the publish flow.
- A different model behind the generator. The old app called Gemini 2.5 Pro, later 3.1 Pro, to write the sites. The rebuild routes to Claude, with a cheap fast model handling routine edits and the stronger one handling initial generations and anything that smells like a redesign. More on this at the end, because it's the part I'm least settled on.
- Migrating users; I took a store of the media files, user / data tables, and copied them all over to the new R2 structures. This was only possible because the data was all sitting at rest, and i switched over in an instant.
The security pass, since "AI rewrote my app" invites the question
A one-day rewrite of an app that holds OAuth tokens and takes money deserves suspicion, so here's what the paranoid parts look like.
The things worth stealing are the per-user GitHub tokens, and those are AES-256-GCM encrypted at rest, with the key living only in the VM's secret store. That carries over from the old app; what changed is scope. The old app requested delete_repo so it could clean up repos when you deleted a project. The rebuild never deletes your repo under any circumstances, because the entire premise is that the repo is yours, so it no longer asks for the permission at all. Deleting a project deletes my record of it and nothing of yours. It's the rare security improvement that's also a philosophy improvement.
Stripe webhooks are signature-verified with a body-size cap, and crediting is idempotent against a unique session index, so a replayed webhook can't double-credit. All credit movement goes through an atomic ledger; nothing writes a balance directly. The expensive endpoints are rate-limited per authenticated user. Production boot does strict validation of every required secret and refuses to start half-configured, while local dev is allowed to boot partial so I can work on generation without Stripe keys present. Generated sites preview inside a sandboxed iframe. And because generations cost real money in both credits and API calls, there's a crash-recovery sweep that runs on boot and every ten minutes after, finding generations stranded by a dead process, failing them, and refunding the credit.
None of this was heroic. Most of it was me asking, one system at a time, "what did the old app get right here, and what did it get right only because Replit was holding it?"
How I checked a rewrite I didn't type
The verification was three layers. The parity table was the first: every old feature audited against the new code, with the deltas written down, including the deliberate ones. The second was tests with a specific flavor I'd recommend to anyone doing a conversion like this: alongside normal unit tests, there are what I ended up calling legacy-reproduction tests, which encode lessons the old app learned in production. The old app once broke because R2 rejects presigned uploads when a header isn't passed back verbatim; there is now a test that exists purely to remember that. The third layer was live verification of the flows where a mistake costs money or trust: real Stripe test purchases end to end, real uploads through the two-phase flow, a real publish to a real repo.
The old app was the oracle. That's the thing a rewrite has that a first build never does, and it's worth more than any amount of model capability.
The unsettled part
Here's the wrinkle I didn't expect. Fable rebuilt the application better than I built it the first time, and I have no urge to argue otherwise. But the product of the application, the actual single-page sites the generator produces, I'm not sure Claude wins.
Claude has taste, and it's the same taste every time. Give it a single-page brief and you can feel it steering toward its idea of a good website, which is a genuinely good website, tastefully typeset, well structured, a little safe. Gemini is messier and more suggestible, and for a tool whose entire job is to make your very own web site, suggestible might be the better temperament. I wrote a pile of prompt scaffolding to fight this, opinionated per-template design systems with baked palettes and structure contracts, and it helps. But I'm still weighing switching the generator back to Gemini while keeping everything else exactly where it is. GLM 5.2 is also genuinely in the conversation now, which is its own small essay about how fast the floor is rising. The only reason it isn't running the generator today is that I have Claude credits.
That's the part of this I keep turning over. The coding model and the product model are different jobs, and the best model for writing my app turned out not to be, automatically, the best model for being my app. Three months became a day, the stack got simpler, the security got tighter, and the one decision I'm still unsure about is the one that was a single line in the config.
Member discussion