RORK LABJP
ENGINE — Rork Max generates code on top of Claude Code and Claude Opus 4.6, which is worth knowing when you tune how specific your prompts areSPLIT — Rork Max is Apple-only. If you also need Android, the original Rork is the one that generates cross-platform apps with React NativeDEVICE — Rork Max targets iPhone, iPad, Apple Watch, and Vision Pro, reaching territory React Native struggles to coverCREDIT — Billing runs on credits: one credit per AI interaction, reset on the 1st of each month with nothing carried overPLAN — Free gives 35 credits a month (5 per day); Junior is $25/mo, Senior $100/mo, and Rork Max $200/mo, with Senior the usual pick for MVP workFUND — Rork raised $2.8M from a16z and now draws over 743,000 monthly visitsENGINE — Rork Max generates code on top of Claude Code and Claude Opus 4.6, which is worth knowing when you tune how specific your prompts areSPLIT — Rork Max is Apple-only. If you also need Android, the original Rork is the one that generates cross-platform apps with React NativeDEVICE — Rork Max targets iPhone, iPad, Apple Watch, and Vision Pro, reaching territory React Native struggles to coverCREDIT — Billing runs on credits: one credit per AI interaction, reset on the 1st of each month with nothing carried overPLAN — Free gives 35 credits a month (5 per day); Junior is $25/mo, Senior $100/mo, and Rork Max $200/mo, with Senior the usual pick for MVP workFUND — Rork raised $2.8M from a16z and now draws over 743,000 monthly visits
Articles/Dev Tools
Dev Tools/2026-06-12Advanced

Schema Versioning for Local Data in Rork Apps — Shipping Updates Without Wiping a Single Favorite

How I stopped losing users' locally stored data when shipping updates to Rork apps. A complete TypeScript migration runner with envelope versioning, backup keys, fixture tests, and the rule that keeps EAS Update schema-neutral.

Rork521AsyncStorage10Data MigrationEAS Update6Long-term Maintenance2

Premium Article

Last winter, the morning after I shipped an update to one of my wallpaper apps, two one-star reviews were waiting for me. Both said essentially the same thing: "All my favorites are gone."

The cause was a change in storage format. I had been saving favorites in AsyncStorage as a plain array of wallpaper IDs (string[]), and the update switched to an array of objects carrying a timestamp. The new code could only read the new shape, so it overwrote the old data with an empty default. Favorites that users had collected over months disappeared because of roughly ten lines I changed.

A server database would never fail this way. You write a migration, run it before deploy, and roll back if it breaks. That discipline is taken for granted on the backend. Yet a surprising number of apps — mine included, at the time — operate local on-device data with no such discipline at all.

After that morning, I moved every app I build with Rork onto a shared migration layer. What follows is the full implementation, plus the operational rules that settled into place while running several apps in parallel over the long term.

Why Local Data Breaks More Easily Than a Server Database

A server migration runs once, against one database. Local data is different. There are as many databases as there are devices, and each one migrates at its own moment, from its own starting version. Devices jumping three versions at once are not rare at all.

And you cannot reach any of them. There is no production server to SSH into. The only repair tool you have is "ship fix-up code in the next update" — and by the time it arrives, the user may have already deleted your app. As an indie developer, you are also the support desk that receives the complaint.

Rork apps, being Expo-based, add one more wrinkle: OTA delivery through EAS Update. Replacing only the JavaScript without store review is a real advantage, but it also raises the frequency of the "new code, old data" state. Because OTA rollbacks are instant too, you can even get the reverse mismatch: "old code, new data."

Building with an AI tool amplifies all of this. Ask Rork to "add a saved-at timestamp to favorites" and it will happily rewrite the types and the UI in one pass. What it will not consider — unless you tell it — is the old-format data already sitting on users' devices. The faster you can change code, the more often code and data drift apart. That is exactly why the discipline around local data needs to be in place first.

The Core of the Design Is a Single Integer — Envelopes and Schema Versions

The rule I apply across all my apps is simple: every JSON value goes into an "envelope" before it is stored.

{ "v": 3, "data": [{ "id": "w_201", "addedAt": 1749690000000 }] }

The shape of the payload (data) is free to change as often as it needs to. In exchange, every shape change bumps the envelope version v by one and comes with exactly one function that converts v2 data into v3 data. At read time, you apply the conversion functions in order, from the stored version up to the current one. That is the whole design.

One detail matters: do not encode the version into the key name. Once you start creating keys like favorites_v2, you end up managing migrations and stale-key cleanup as two separate problems, and soon nobody can say which key is authoritative. Keep one key, and let only the integer inside the envelope move forward. In my experience this is the shape least likely to produce accidents.

The earlier question — whether to use AsyncStorage, MMKV, or SQLite in the first place — is covered in my comparison of the three local storage options, so here I will stay focused on protecting the data after you have chosen.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A complete TypeScript migration runner that wraps AsyncStorage values in a versioned envelope
The operational boundary that keeps EAS Update (OTA) schema-neutral, and the rollback failure scenario behind it
A measurement recipe for deciding how long to keep backups and when old migration functions can be retired
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-07-14
Designing Seams That Survive AI Regeneration in Rork
Every follow-up prompt to Rork can quietly wipe out logic you wrote by hand. Protecting it with prompts is a patch, not a fix. Here is how to separate generated code from code you own, and draw a boundary that regeneration cannot reach, with working Zustand and service-layer examples.
Dev Tools2026-06-28
Ship EAS Updates to a Few First, and Halt Automatically on Crash Rate
Because OTA updates reach everyone instantly, a bad update reaches everyone instantly too. Here is a three-layer design: ship EAS Update to a small canary, decide expand-or-halt from crash-free rate automatically, and hold a safety net on the device — with working code.
Dev Tools2026-06-24
When EAS Update Ships but the Bug Won't Die — Why OTA Stalls Silently, and How I Operate Around It
EAS Update can succeed and still fail to reach a slice of your users. These are field notes on runtimeVersion drift, updates that publish but never get adopted, and choosing the right rollback — with the instrumentation that actually helped on my Rork apps.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →