React Native at 500k+ installs and enterprise SLAs is a different discipline than React Native at prototype scale. The framework's abstractions that accelerate early development become the exact things you need to work around for production performance.
Memory Is the Real Constraint
On mobile, memory pressure kills apps before CPU does. The patterns that cause problems at scale:
Image caching without eviction. Most image libraries cache aggressively by default. At 500k+ installs across varied device tiers, a 512MB device running your app alongside other apps will OOM if you're holding 200MB of cached product images.
We implemented a tiered cache with LRU eviction based on device memory class:
FlatList Configuration
The default windowSize and maxToRenderPerBatch values are tuned for demo apps. For a product catalog with 10k+ items, we dropped windowSize to 5 and maxToRenderPerBatch to 3. Memory footprint dropped 25%.
Bridge Serialization
Every piece of data crossing the bridge gets serialized to JSON. Passing a 2MB payload from native to JS is not a 2MB operation. It's a serialize-copy-deserialize cycle that can spike memory 4-6x.
We moved large data transfers to shared memory via JSI:
OTA Delivery That Doesn't Break Production
Over-the-air updates (CodePush or custom) are powerful and dangerous. A bad OTA update pushes broken code to every user instantly, with no app store review as a safety net.
Our pipeline: staged rollout with automated crash-rate monitoring at each stage. If crash rate exceeds baseline by 0.5%, the rollout pauses and alerts fire. Rollback is automatic.
The CI/CD pipeline runs the full test suite, then builds platform-specific bundles, signs them, and uploads to the staging CDN. Promotion from staging to production is a manual gate. Someone has to look at the metrics and approve.
Need a second opinion on your mobile engineering architecture?
I run free 30-minute strategy calls for engineering teams tackling this exact problem.
Book a Free CallFrame Drops Tell the Truth
We built a custom frame-drop monitor that samples the JS thread and UI thread frame rates every 100ms during critical user flows:
The biggest offenders were always the same: synchronous storage reads during render, layout thrashing from dynamic style calculations, and unnecessary re-renders from poorly memoized selectors.
The fix is boring but effective: useMemo and useCallback everywhere they matter, React.memo on list item components, and moving all storage reads to initialization rather than render time.
The Metrics That Matter
For enterprise mobile at scale, we track five non-negotiable metrics. Everything else is noise until these are green:
| Metric | Target | Measurement |
|---|---|---|
| Crash rate | < 0.1% | Firebase Crashlytics, daily |
| ANR rate | < 0.5% | Play Console / Xcode Organizer |
| Cold start | < 2s | Custom P95 instrumentation |
| JS bundle size | < 5MB compressed | CI build artifact check |
| Memory high-water | Device-tier dependent | Custom native module |