Most mobile developers know about TestFlight. Fewer fully exploit what happens when you combine it with EAS Update. The combination unlocks a distribution loop that’s genuinely fast: get the app to real users the same day, then keep improving it without any rebuild or resubmission.
This is how I use the combo now, and why it changed how I think about mobile iteration.
The Problem with Normal Mobile Releases
On the web, you push a change and it’s live in seconds. On mobile, the baseline expectation is days:
- Build the binary (10–30 minutes)
- Submit to App Store or Play Store
- Wait for review (24 hours minimum, often longer)
- Users update manually or wait for auto-update
Even with best practices, you’re looking at a day between writing code and getting feedback from a real user. That gap kills momentum, especially early in a project when you’re still figuring out what the product should be.
The Combo: Two Tools, Two Jobs
The trick is to separate initial distribution from ongoing iteration, and use the right tool for each.
Step 1: TestFlight for Initial Distribution
TestFlight is Apple’s beta distribution platform. The key property that makes it powerful: it does not require App Store review. You submit a build, Apple does a basic check (usually complete in under an hour), and you can immediately invite specific users by email.
For Android, Google Play’s Internal Testing track works the same way — no review required, instant invite via email.
This gets the app into the hands of real testers the same day you build it, with no review waiting period.
What TestFlight gives you:
- Real device testing without App Store review delay
- Invite testers by email (up to 10,000 for public links, or specific addresses for internal groups)
- Testers install directly from the TestFlight app — no sideloading, no MDM
- Crash reports and basic feedback built in
- Up to 90 days per build before it expires
You only need to do this build-and-submit step once per meaningful change to native code. For everything else, there’s EAS Update.
Step 2: EAS Update for Everything After
EAS Update pushes over-the-air (OTA) updates to your Expo app without rebuilding the binary. You change your JavaScript/TypeScript code, run one command, and users get the update the next time they open the app (or immediately, if you implement foreground update checks).
eas update --branch preview --message "fix onboarding flow"
That’s it. No build. No submit. No review. The update goes out in under a minute.
What EAS Update gives you:
- Ship JS/TS changes in seconds
- Target specific branches (production, preview, internal)
- Rollback instantly if something breaks
- Update policies: lazy (on next launch) or immediate (prompt user in-app)
The constraint: EAS Update can only change JavaScript and assets. Native code changes (new native modules, permissions, splash screen, app icon) still require a new binary and a new TestFlight submission. But in practice, once you’ve set up your native layer, you rarely need to touch it.
The Full Loop in Practice
Here’s what a typical iteration day looks like with this setup:
Day 0 (setup, once):
- Build the app binary with
eas build --platform ios --profile preview - Submit to TestFlight:
eas submit --platform ios - Invite testers by email from App Store Connect
- Testers install via TestFlight link
Every iteration after that:
- Write code
- Run
eas update --branch preview --message "what changed" - Tell testers to reopen the app
- Collect feedback
The gap between writing code and getting feedback collapses from 24+ hours to under 10 minutes.
Setting Up EAS Update
If you’re already using Expo, getting EAS Update working is straightforward.
Install and configure:
npx expo install expo-updates
eas update:configure
This adds the necessary native configuration. You’ll need to do one new build after this to bake in the update runtime, but that’s the last build you need for a while.
Add update checks to your app (optional but recommended):
import * as Updates from 'expo-updates';
async function checkForUpdate() {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
}
}
Run this on app foreground and you get instant updates without asking users to manually reopen the app.
Why This Changes the Feedback Loop
The slow part of mobile development isn’t writing code. It’s the gap between writing code and learning whether it worked. TestFlight + EAS Update compresses that gap to near zero.
In practice, this means:
- You can test on real devices constantly, not just in the simulator
- You can send a fix to a tester within minutes of hearing about a bug
- You can A/B test flows by pushing updates to different branches
- You can iterate on UX without rebuilding — change a layout, ship it, watch someone use it, change it again
This is the web development experience, applied to native mobile. Not quite instant, but close enough that it stops being a bottleneck.
Limitations to Know
EAS Update is JS-only. Any change that touches native code needs a new binary. Common triggers: adding a native module, changing app permissions, updating the splash screen, changing the bundle ID. Keep a mental model of what’s native vs. what’s JS and you’ll know when you need a full rebuild.
Build compatibility. An update must be compatible with the native binary it targets. EAS manages this automatically by matching runtime versions, but it means you can’t push an update built against a newer Expo SDK to a binary built against an older one.
TestFlight expiry. Builds expire after 90 days. For a live product, you’ll be rebuilding more frequently anyway, but for a slow-moving internal test, set a calendar reminder.
iOS only for TestFlight. For Android, use Google Play’s Internal Testing track. Same idea, slightly different setup, but equally fast — no review required.
The Point
If you’re building a mobile app and you’re not using OTA updates, you’re paying a tax on every iteration. TestFlight removes the review bottleneck for initial distribution. EAS Update removes the rebuild-and-submit bottleneck for every change after that.
Together, they make mobile iteration feel like web iteration. Ship early, get real feedback, fix what matters, repeat — all in the same day.