We’re live on Product Hunt — click here to upvote

Inside RapidNative's Version History: How We Track Every AI Generation

SA

By Suraj Ahmed

12th Jul 2026

Last updated: 12th Jul 2026

Inside RapidNative's Version History: How We Track Every AI Generation

The scariest button in any AI app builder is the one that says "regenerate." You spent forty minutes iterating on a login screen. It's almost right. You ask the AI to nudge the button color and — because the model got a little too excited — it also refactors the file structure, rewrites your navigation, and changes the auth flow you just approved. Cmd+Z is not going to save you.

This is the problem every AI code generation product has to solve, and most of them solve it badly. Some treat "version history" as a single-file undo. Some snapshot the entire project on every keystroke and pay for the storage in bandwidth and latency. A few just hope you were paying attention.

At RapidNative, our AI mobile app builder turns natural language into production React Native + Expo apps, and we've had to think hard about this. This post is a technical deep dive into how our AI code generation version history actually works — the two-layer data model, the message replay engine, the trick that saves your package.json dependencies, and the design decisions we made along the way.

Developer reviewing code diffs on a laptop Version history is the single most important safety net in AI-driven development — Photo by Christina Morillo on Unsplash

Why "undo" doesn't work for AI-generated code

Traditional editors can afford to think about history as a linear stack of keystrokes. Each keystroke is small and reversible, so Cmd+Z pops the top of the stack and life continues.

AI code generation breaks that model. A single prompt can:

  • Create three new files.
  • Delete two others.
  • Rewrite six existing files in ways that reference the new ones.
  • Add four dependencies to package.json.
  • Reshape the folder structure.
  • Change the app name.

There is no single "keystroke" to undo. And the changes aren't independent — reverting one file without the others produces a broken build. The unit of change is the generation, not the character.

So the real question is: how do you capture the world at every generation cheaply enough that users don't feel it, and durably enough that they can walk backward through their entire creative history?

That is what our version history system does. Here's how.

The two-layer model: messages + files

Every version history system has to answer two questions:

  1. What did the user ask for? (the intent)
  2. What was the world after we answered? (the state)

Most systems try to conflate the two. Git, for example, records both the diff and the commit message in one atomic unit. That works great for humans typing curated commits. It works poorly for AI, where the intent (the prompt) and the result (the files) come from different processes and need to be revised independently.

We split them into two layers.

Layer 1 — Messages: the intent stream. Every user prompt, every AI response, and every system-triggered action lives in the messages table, ordered by created_at. This is our append-only conversation log. Nothing is ever deleted.

Layer 2 — Files: the current world. The files table (which the app treats as project_files) holds exactly one row per file path per project, with a UNIQUE(project_id, file_path) constraint. This is the current state. Not the historical state. Not the diff. Just what the code looks like right now.

If you're familiar with event sourcing, this will feel familiar. The messages table is the event log. The files table is the materialized view. The current state is a projection you can rebuild at any time by replaying the events. That single decision — treat messages as the source of truth and files as a projection — makes almost everything else possible.

Anatomy of a message

To make replay work, a message can't just be a blob of text. It has to carry enough structure that we can programmatically re-apply it later. So our AI responses are streamed as text but structured internally as blocks:

<CodeProject>
```json file="app.json"
{ "expo": { "name": "MyFitnessApp" } }
export default function Home() { ... }
Before:
Start now

Ready to build your app?

Turn your idea into a production-ready React Native app in minutes.

Free tools to get you started

Questions

Frequently asked questions

What is RapidNative?

RapidNative is an AI-powered mobile app builder. Describe the app you want in plain English and RapidNative generates real, production-ready React Native screens you can preview, edit, and publish to the App Store or Google Play.

Can I export the code?

Yes. RapidNative generates clean React Native and Expo code that you can export at any time. No lock-in, no proprietary format. Hand it to your developers or keep building inside RapidNative.

Is RapidNative free to use?

Yes. You can build apps on the free plan with no credit card required. Paid plans unlock unlimited AI generations, code export, and direct publishing to the App Store and Google Play.

Do I need to know how to code?

No. Most users build apps by describing what they want in plain English. Developers can drop into the code whenever they want more control, but coding is optional.

How long does it take to build an app?

Most users have a working first screen in under a minute. A full MVP usually takes a few hours instead of the weeks or months traditional development requires.