← Volver al blog

April 5, 2026·10 min read

Upgrading Expo SDK without the weekend meltdown

Major SDK bumps are integration tests for your discipline. The recipe: read the changelog, upgrade the Expo package, run the doctor, fix TypeScript, regenerate native if needed, then test on device—not just web. Skipping steps is how “it works on my machine” becomes “it ships broken.”

Before you start

Commit a clean tree. Note current versions. Skim breaking changes for libraries you actually use—not the whole ecosystem.

Upgrade one major at a time if you’re far behind.

During

Follow the official upgrade guide commands; they exist because someone learned the hard way. Clear caches when native deps shift.

Fix deprecations the release warns about; don’t silence them.

After

Run E2E smoke, test push, maps, camera—whatever’s native in your app. Ship a beta to internal testers.

Document gotchas for the next person.

If something still breaks

Bisect: does a fresh `expo prebuild` repro? Does a clean install? Compare lockfile diffs. Search issues with your exact SDK plus library versions before you fork a dependency.

Keep a short internal log of each upgrade—future you will not remember which Pod tweak fixed Xcode 17.

Risk assessment before touching `package.json`

Upgrades are integration projects. Read the entire Expo release notes, not only the headline features—breaking changes hide in bullet lists. Inventory native modules you rely on: camera, maps, payments, push. Check each maintainer’s compatibility matrix for the target SDK. If you lag multiple majors, plan stepping stones—two one-major upgrades may cost less calendar time than one giant leap with a week of unknowns. Align stakeholders: freeze feature work that touches native code during the upgrade window. Communicate to support that odd crashes may spike briefly—give them a build number cheat sheet. Back up credentials and signing keys before regenerating native projects—nothing stalls a release like a misplaced provisioning profile.

Execution order that minimizes thrash

Bump Expo and React Native together per guidance, then align community packages. Run `expo doctor` after each logical step; fix warnings while context is fresh. Regenerate native projects with prebuild in a clean git state so diffs are reviewable. If you maintain custom native code, merge carefully—three-way merges on Xcode projects are painful; consider regenerating and reapplying patches. Update TypeScript and ESLint configs when templates change. Run `tsc` and unit tests before launching simulators—fast failures save hours. Clear Metro and watchman caches when transforms change. Document pod install and Gradle sync steps for teammates on different machines—Apple Silicon versus Intel still matters for some native deps.

Validation beyond ‘it launches’

Smoke test push notifications, deep links, IAP, and camera on real devices—emulators miss vendor quirks. Compare bundle sizes before and after; unexpected growth hints at duplicate dependencies. Profile startup time; Hermes upgrades sometimes shift cold start. Test OTA update compatibility if you use it—runtime version must match native capabilities. Verify Proguard/R8 rules on Android release builds if you use minification—reflection-heavy libraries break silently. Run accessibility spot checks—RN upgrades occasionally tweak default focus or touchable behavior. If you ship to enterprise MDM environments, test on a managed device profile if possible.

Release coordination and rollback

Stage store releases with phased rollouts when stores allow. Monitor crash-free sessions and ANRs for the first 48 hours aggressively—set alerts on thresholds you would act on during the day, not only next sprint. Prepare rollback: previous store version binaries, OTA channel pinning, and feature flags to disable new native-dependent features without a full revert. Communicate with marketing about what users will see—release notes should match reality. If a blocker emerges, freeze further merges to main until resolved; parallel feature work confuses hotfixes.

Post-upgrade learning loop

Run a retrospective: what surprised us, what docs were missing, how long tasks actually took. Update internal upgrade playbook with commands, gotchas, and timings. Contribute upstream issues or docs when you find gaps—Expo and RN communities benefit. Schedule the next upgrade earlier while memory remains—debt accumulates faster than teams expect.

Shipping and reliability habits (1)

Analytics schema governance prevents warehouse disasters: version events, avoid high-cardinality strings, and align names across iOS, Android, and web. Consent gating must stop network calls, not just UI. Separate dev and prod projects to avoid polluting dashboards.

Testing onboarding changes with funnel metrics beats debating opinions. Segment by acquisition channel and platform; back behavior differs. Skip paths must be genuine—dark patterns may win short metrics and destroy brand trust. Localization length tests prevent clipped CTAs in verbose languages.

Platform differences worth rehearsing (2)

Environment variables should be classified: public-by-design, sensitive-with-mitigations, or never-on-device. `EXPO_PUBLIC_` values are extractable—treat them that way. Align env handling across EAS profiles and local dev; fail fast when keys are missing instead of shipping undefined behavior.

Project structure should make ownership obvious: routes as backbone, feature folders for product areas, thin screens, and shared infrastructure that is deliberately named. Refactor in vertical slices with device-tested releases—big-bang rewrites without tests are how teams lose weeks.

Security, privacy, and data handling (3)

Accessibility is compatibility. Labels, focus order, and dynamic type are not polish—they determine whether users can complete tasks at all. Test with VoiceOver and TalkBack on hardware; simulators miss focus bugs. When designs prioritize minimalism, negotiate text alternatives for icon-only controls. Accessibility regressions often follow navigation redesigns—add checklist items to those PRs specifically.

Internationalization is a product feature, not a string swap. Plural rules, RTL layout, and locale-aware formatting change behavior—not just copy length. Pseudolocale helps find clipping early, but real Arabic and German QA catches nuance. Avoid concatenating translated fragments; context matters. Document glossary terms so translators do not invent inconsistent product names.

Performance and measurement discipline (4)

Images and maps look simple in mocks and expensive in production. Decode sizes should match display sizes; static map thumbnails are not interchangeable with interactive MapView gestures. Quotas, API keys, and offline behavior need explicit fallbacks—addresses as text, retry buttons, and calm error copy. Monitor vendor dashboards for spikes that indicate bugs or abuse.

Error boundaries catch render failures, not native crashes or async mistakes. Pair them with platform crash reporting and structured client logs. Fallback UI should include build identifiers and humane copy—never raw stack traces for end users. Test fallbacks with screen readers; a broken error screen is still broken UX.

Team process and long-term maintenance (5)

Keyboard and form UX separate polished apps from ‘works on desktop simulators.’ Platform differences in soft input modes matter; test smallest phones and Android gesture navigation. Primary actions must remain reachable when the keyboard is visible—scroll containers and keyboard controllers exist because this problem is universal.

Type-safe navigation pays off when routes multiply. Keep param lists near navigators, validate external URLs, and avoid serializing non-JSON-safe values through params. Renaming routes is a cross-cutting change—update analytics, push payloads, and E2E selectors in the same release train.

Shipping and reliability habits (6)

Privacy nutrition labels and Play Data Safety forms should reflect actual SDK behavior—inventory dependencies each release and remove dead code. Drift between claims and telemetry is legal and store risk, not just embarrassment. Involve legal early when adding analytics or ads.

Expo SDK upgrades are integration projects: `expo doctor`, aligned community packages, regenerated native projects, and device smoke tests for camera, push, and IAP. Freeze unrelated native refactors during the upgrade window and keep rollback paths hot. Document surprises for the next upgrade while memory is fresh.

Platform differences worth rehearsing (7)

Background execution policies change with OS updates—revalidate after major iOS and Android releases. Misused background modes invite rejection. Persist user work frequently; the OS can kill you anytime after backgrounding. Uploads and timers should tolerate pause and resume without corrupting state.

Design tokens and semantic colors make dark mode and rebrands feasible. Mixing three styling systems doubles migration cost—pick a primary approach and draw boundaries. Runtime CSS-in-JS can cost frame time on hot screens—profile before adopting wholesale.

Security, privacy, and data handling (8)

Helper modules concentrate glue code—storage, navigation, permissions—so screens stay readable. Split helpers by topic before files become merge-conflict magnets, and document each module’s contract. Good helpers answer ‘where do we save tokens?’ in one glance—not ‘ask Sarah.’

Shipping React Native features is less about any single API and more about the system around it: typed boundaries, predictable navigation, and telemetry that tells you what broke in production. Prefer boring, explicit modules over clever metaprogramming that the next hire cannot grep. When platform vendors change behavior in point releases, your defense is automated smoke tests on real devices and a short internal changelog of native assumptions you rely on.

Performance and measurement discipline (9)

Monorepos amplify both leverage and failure modes: duplicate React versions cause mysterious hook errors, and Metro misconfiguration blocks local packages from resolving. Invest in workspace discipline—single React version, documented `watchFolders`, and lint rules preventing packages from importing app navigators accidentally. CI must mirror local installs; ‘works on my laptop’ with different package managers is a time bomb.

Native modules are product decisions disguised as engineering tasks. You inherit Xcode and Gradle upgrades, store review scrutiny, and security obligations. Prefer maintained Expo modules and config plugins before writing JNI or Swift glue from scratch. When you must go native, budget pairing time with platform specialists and write runbooks for on-call—crashes in native code bypass many JS safeguards.

Team process and long-term maintenance (10)

Storage is not a database. AsyncStorage and MMKV excel at key-value preferences; SQLite or remote APIs belong elsewhere for relational data. Migrations should be incremental, logged, and non-blocking for UI. Secure tokens need secure storage when your model demands it—speed is not a substitute for correctness on auth material.

WebViews are untrusted browsers inside your app. Validate `postMessage` payloads, lock navigation to expected hosts, and prefer system-browser auth flows when OAuth security demands it. Third-party JavaScript can change without your deploy—treat XSS in web as bridge compromise risk. Clear storage on logout and rate-limit message handlers.

Shipping and reliability habits (11)

Metro cache issues masquerade as logic bugs. Establish a documented reset ladder: dev server restart, cache flags, Watchman, derived data, then dependency reinstalls. Compare platforms when only one breaks—native steps diverge. Keep CI caches deterministic with lockfiles and pinned toolchains.

Environment variables should be classified: public-by-design, sensitive-with-mitigations, or never-on-device. `EXPO_PUBLIC_` values are extractable—treat them that way. Align env handling across EAS profiles and local dev; fail fast when keys are missing instead of shipping undefined behavior.

Platform differences worth rehearsing (12)

Internationalization is a product feature, not a string swap. Plural rules, RTL layout, and locale-aware formatting change behavior—not just copy length. Pseudolocale helps find clipping early, but real Arabic and German QA catches nuance. Avoid concatenating translated fragments; context matters. Document glossary terms so translators do not invent inconsistent product names.

Monorepos amplify both leverage and failure modes: duplicate React versions cause mysterious hook errors, and Metro misconfiguration blocks local packages from resolving. Invest in workspace discipline—single React version, documented `watchFolders`, and lint rules preventing packages from importing app navigators accidentally. CI must mirror local installs; ‘works on my laptop’ with different package managers is a time bomb.

Security, privacy, and data handling (13)

WebViews are untrusted browsers inside your app. Validate `postMessage` payloads, lock navigation to expected hosts, and prefer system-browser auth flows when OAuth security demands it. Third-party JavaScript can change without your deploy—treat XSS in web as bridge compromise risk. Clear storage on logout and rate-limit message handlers.

E2E tests should protect revenue paths, not every permutation. Stable selectors (`testID`) beat text that marketing rewrites weekly. Flake management is a feature: quarantine, fix root causes, and keep smoke suites green on CI devices. Five reliable tests beat fifty flaky ones that everyone ignores.

Performance and measurement discipline (14)

In-app purchases require server validation, restore flows, and support tooling that respects privacy. Sandbox quirks are normal—budget QA time. Subscriptions interact with family sharing, regional pricing, and refunds; engineering must stay aligned with finance and legal narratives users see in receipts.

Privacy nutrition labels and Play Data Safety forms should reflect actual SDK behavior—inventory dependencies each release and remove dead code. Drift between claims and telemetry is legal and store risk, not just embarrassment. Involve legal early when adding analytics or ads.

Patrocinado

Promoción breve