52 posts · est. 2024

Ship JS at the
speed of a tweet.

I'm building cross-platform apps with Expo and writing about every load-bearing detail — OTA updates, remote config, version skew, staged rollouts — that lets a two-person team ship bug fixes in 90 seconds instead of waiting on the App Store.

Recentwriting

We killed app-store releases for non-native changes

After moving 100% of JS-only changes to expo-updates, our release cadence went from weekly to ~9 per week and the crash rate dropped. Here's why.

8 min read Read

Staged rollouts: from 1% → 10% → 100% without a release manager

Remote Config does the cohorting, expo-updates does the delivery, and a tiny GitHub Action does the ramp. 40 lines of YAML, zero humans in the loop after merge.

11 min read Read

Auto-rollback when your new bundle crashes on launch

A 60-line crash-counter persisted to MMKV: if the app dies during JS init twice in a row, it nukes the embedded update and boots the last known-good bundle. Saved my weekend in March.

9 min read Read

Versioning hell: matching JS bundles to native runtimes

Why runtimeVersion: "appVersion" is a trap, and the three lines of app.config.ts that fixed our 2.3% "white screen on update" reports.

7 min read Read

Remote Config as a feature-flag service for OTA bundles

Treating Firebase RC as the source of truth for both feature gates and OTA channel routing — same primitives, half the moving pieces, one config file in the admin console.

10 min read Read

When NOT to OTA: the native-bridge gotchas

A taxonomy of changes that look JS-only but reach into native: image cache configs, Reanimated worklets, splash screens, deep-link schemes. Push them through the store, not the wire.

6 min read Read

Tracking OTA adoption with PostHog (and one ugly BigQuery query)

The metric I actually care about: time between bundle publish and 95% device adoption. Median is now 38 minutes. Last year it was 6 days.

8 min read Read

Why we run every PR's bundle through TestFlight before promoting to OTA

Counter-intuitive: even pure-JS changes get a native build on every PR. It catches bridge-incompatible Reanimated upgrades before they reach a single real user.

5 min read Read