The first AppX architecture, and why we tore it down
v1 was a web-first React generator wrapped in Vite hosting. It demoed beautifully and failed in production. The honest autopsy — what stayed, what went, and why the demo and the product are different things.
AppX team ·
AppX did not start out as a mobile app builder. The first version generated React websites. It demoed beautifully, shipped in a few weeks, and reached about fifty internal users who told us, politely and unanimously, that we had built the wrong thing. Thirty days after the first deploy we tore almost all of it down. The login screen survived. The chat shell survived. Just about everything between the prompt and the preview got replaced.
This is the autopsy.
What v1 was
The architecture fit on a napkin. A user typed a prompt. The backend made one LLM call that returned a JSON blob — a map of file paths to file contents — for a complete React webapp. We wrote those files to S3 under the project's slug. A small hosting layer mapped {slug}.appx.uz to the S3 bundle and served it as a static site.
That was the whole thing. Three services. Around forty source files. No edit loop. No build server. No live preview infrastructure beyond "host static files on a subdomain." The LLM was the compiler, S3 was the filesystem, the subdomain was the runtime.
It looked clever on a whiteboard. One call in, one website out.
Why it felt right at first
The demo was clean. A friend typed "make me a portfolio site for a freelance designer" and twelve seconds later we sent them a link. The link worked. The site had pages, a nav bar, a contact form that did nothing but looked like it would. Generation cost roughly ten cents per app — cheap enough that we could be generous with retries.
Internal feedback was warm. About fifty people had a project in the dashboard within the first week. They opened the link, screenshotted the result, sent it to friends. We mistook the screenshot for the signal. The screenshot was the signal that the demo was good. The product signal was somewhere else entirely, and we did not hear it for almost three weeks.
The hidden problems
The first problem was the most embarrassing because it was the most obvious in retrospect. Users did not want websites. Every time we asked someone what was missing, we got the same answer, in slightly different words: I want it on my phone. Not "I want a mobile-responsive site." Not "make the buttons bigger on mobile." They wanted an app. They wanted to scan a code, install something, hand the phone to a friend, watch the friend tap around. A URL did not satisfy that craving. A URL is a thing you look at. An app is a thing you have.
We had built a generator for the wrong artifact. No amount of prompt engineering was going to turn a React webapp into the thing the user was visualizing when they typed their idea.
The second problem was structural. Single-shot generation collapsed past roughly three screens. When the LLM had to produce a small app it did fine. When it had to produce a routing tree with shared components, a theme system, four or five pages, and consistent imports across all of them, the wheels fell off. Type imports pointed at files that did not exist. Routes referenced folders the generator had not created. Theme tokens were declared in one file and then duplicated as literal hex codes throughout the rest. The model could write any one file beautifully. It could not hold the whole project in its head at once.
The third problem was the deal-breaker even if the other two had been solvable. There was no edit loop. Users typed a prompt, got a result, and immediately wanted to change it. The only verb v1 understood was regenerate. Every change meant a new single-shot call against a new prompt, paying the full token cost, hoping the model produced something close enough to the previous version that the change felt incremental. It almost never did. The "fix the button color on the third page" request would return a site that looked completely different. Users lost their work to every iteration. Success rates dropped on every round.
There was a quieter fourth problem hiding underneath all of this: the web stack was the wrong target. Even if we had solved single-shot coherence and built a working edit loop, we would have ended each user session with a website. The product they were asking for did not live at the end of that road.
The tear-down: what stayed, what went
We kept the parts of v1 that were about who the user is and threw out everything that was about what the user makes.
Stayed: authentication, user accounts, the project metadata schema, billing, the chat UI shell. These had no opinion about whether we were generating websites or apps. They were correctly factored. We have edited them a great deal since, but never had to replace them.
Replaced: the generation pipeline went from single-shot to plan-then-build — an architect LLM emits a plan, an editor LLM applies it file by file, both rounds informed by what is actually on disk. Code storage went from an S3 dump to a file-server backed by Git, so we could read individual files, diff them, snapshot them, and revert them. The preview surface stopped being a static subdomain and became a per-project container running Metro, the React Native bundler, serving an Expo Go preview the user could open by scanning a QR code on their phone.
Built new: the edit engine that drives iterative changes against an existing project. The warm pool that keeps containers pre-provisioned so generation-to-phone takes seconds and not minutes. The bundle server that serves Metro's protocol to Expo Go. Forge, the orchestrator that schedules containers across hosts and tracks their lifecycle. None of these existed in v1 because v1 did not need them. v2 does not work without any of them.
The login button is the same login button. Almost nothing behind it is.
The lesson
The demo and the product are different things, and the difference is structural.
A demo can fake structure with one big LLM call because nobody is going to iterate on it. The demo's job is to be impressive for thirty seconds. A single-shot generation that produces a coherent-looking artifact, on the first try, with no follow-up, is sufficient. The artifact does not have to be correct under editing. It does not have to survive the user changing their mind. It just has to look right long enough to make the case.
A product has to survive iteration. Users will edit. Users will misunderstand what they asked for and ask for the opposite. Users will run the same project for six weeks and accumulate fifty small changes. Every one of those edits has to land on a structure that is real — files that exist on a filesystem, types that resolve, routes that match folders, a theme that lives in one place. The demo can paper over the absence of that structure with a confident-looking JSON blob. The product cannot.
We rebuilt because the product needed structure to be real, and durable, and correct under change, and the v1 architecture had none of those properties. It had impressive output. That is not the same thing.
Closing
We do not regret v1. Building it taught us what the product was, by failing in exactly the ways a single-shot web generator would fail. Fifty users told us we had aimed at the wrong screen. The thirty days we spent on v1 saved us from spending six months adding edit loops and routing fixes to an architecture that was pointed at the wrong target the whole time. The fastest way to find out you are building the wrong thing is to build it badly, ship it to people who will tell you the truth, and listen.
The login button still works. Everything behind it now generates an app that goes on your phone. That is the only sentence about AppX that mattered then and the only one that matters now.