skip to content

Before shipping a React Native plant-identification app's camera feature, why run a release build on a physical phone, and how do you run one?

level: seniorimportance: should knowfreq 40%

answer

  1. what differs besides speed?
  2. no Metro, Hermes bytecode, __DEV__ false
  3. DevTools and Dev Menu disabled
  4. --mode release, --variant release
  5. Release build configuration in Xcode

basics

~20 s

Release builds bundle Hermes bytecode, drop dev-only code and the dev server, and may shrink native code, so some bugs and all real performance appear only there. Run one with --mode release (React Native CLI) or expo run with --variant release or --configuration Release.

solid answer

~50 s

Some bugs only exist in the build users get: there's no Metro, the bundle is Hermes bytecode, `__DEV__` is false, the Dev Menu and DevTools are gone, and on Android shrinking can strip classes native modules need. Debug is also much slower on the JavaScript thread, so the camera flow's real speed only shows in release. For a React Native CLI app I run `npm run android -- --mode="release"` (which needs release signing set up), and on iOS I set the Xcode scheme's Run build configuration to Release and run on the phone; in an Expo project, `npx expo run:android --variant release` and `npx expo run:ios --configuration Release --device`. Then I check permissions, capture in real light, processing time on a low-end Android, cold start and poor networks, reading native logs and symbolicating stacks, since DevTools can't attach.

code

bash · 7 lines
bash
# React Native CLI project
npm run android -- --mode="release"
npm run ios -- --mode="Release"   # iOS Release configuration; for a phone, Xcode's scheme route is the documented one

# Expo project with native directories (prebuild or development build)
npx expo run:android --variant release
npx expo run:ios --configuration Release --device

go deeper

for a junior

Recall the commands for release runs and that DevTools and the Dev Menu are unavailable there.

for a middle

Explain the differences between debug and release builds that cause release-only bugs: no Metro, Hermes bytecode, DEV, native shrinking.

for a senior

Run a structured pre-release device check for hardware-heavy flows, and debug release-only crashes with device logs and symbolication rather than guesswork.

for a principal

Make release-on-device checks a required gate for hardware-dependent features and decide which devices the team must cover.

## The scenario A plant-identification app lets users photograph a leaf and get a species name. The camera screen, the upload and the result screen all work in debug builds on a phone. Before release, the team needs to know the app behaves the same way **in the build users will actually get**. That means running a **release build on a physical device**, because some bugs only exist there. ## Why release-only bugs exist A release build differs from a debug build in ways that change behaviour, not just speed: - **No dev server.** The JavaScript is bundled into the app, so any code or configuration that assumed the dev server was there stops working. - **Hermes bytecode.** The bundle is precompiled to Hermes bytecode at build time rather than served as source by Metro, and it is the exact artifact users run. - **Dev-only code is gone.** `__DEV__` is false, development warnings disappear, and the Dev Menu, LogBox and React Native DevTools are disabled. - **Native optimisation.** On Android, if shrinking is enabled, classes that native modules reach through reflection can be stripped or renamed, a classic release-only crash. - **Real performance.** Debug builds are much slower on the JavaScript thread; release is the only honest measure of whether image capture and processing feel instant. ## How to run a release build on a device | Project | Android | iOS | |---|---|---| | React Native CLI | `npm run android -- --mode="release"` | In Xcode, set the scheme's Run build configuration to Release and run on the phone; `npm run ios -- --mode="Release"` builds the same configuration from the CLI | | Expo (prebuild / dev build) | `npx expo run:android --variant release` | `npx expo run:ios --configuration Release --device` | Points worth knowing: 1. On Android, the React Native docs say `--mode release` is only available once release signing is set up, and recommend uninstalling any previous version first. 2. For iOS in Xcode: **Product → Scheme → Edit Scheme**, choose the **Run** tab and set **Build Configuration** to `Release`, then run on the phone. The Dev Menu is disabled and the JavaScript is bundled locally, so the phone can be tested away from the computer. 3. Expo's docs describe `run:android --variant release` and `run:ios --configuration Release` as ways to test bugs that only show up in production builds; they are not code-signed for store submission. 4. Metro can be stopped: the docs point out that all JavaScript is bundled into the APK. ## What to check on the device For the plant-identification flow: - **Permission flow**: first launch, denial, and granting later from Settings. - **Capture**: focus and exposure in real light, portrait and landscape, and the front camera if offered. - **Processing**: time from shutter to result, and memory behaviour with full-resolution photos on a low-end Android. - **Cold start into the camera screen**, since release start-up differs from debug. - **Poor network** while uploading the photo. ## Debugging what you find Release builds are hard to inspect, since DevTools cannot attach. The usual approach: - Reproduce in a debug build first; if the bug disappears, the difference between the builds is itself the clue. - Read native logs from the device, such as Android's logcat or the iOS device console, for crashes and JavaScript exceptions. - Symbolicate any JavaScript stack with the source map from the same build. - On Android, the `debugOptimized` build type (React Native 0.82, backported to 0.81) gives near-release native performance while staying debuggable, which helps separate "slow because debug" from "slow for real". ## Making it routine A one-off release run before a big launch catches less than a habit does: - run the release build on a device for every change that touches native modules, permissions or the camera; - keep one low-end Android phone permanently available for these runs; - keep the release-run commands in the project's README so anyone on the team can do it. ## Where this sits Running release builds on devices is a pre-release check, not the store release itself: signing for distribution, uploading and store review are separate steps.

  • The app crashes on launch only in the Android release build. What do you check first?
    Read logcat from the device for the exception. A JavaScript error needs symbolicating with that build's source map; a Java or Kotlin error such as a missing class often points at shrinking removing something a native module uses through reflection. Then reproduce with a debug build: if it works there, compare what the release build changes.
  • Why not just use the Android debugOptimized build type instead of release for performance checks?
    debugOptimized, added in React Native 0.82, optimises the native C++ libraries while staying debuggable, so it's a better development baseline than plain debug. But it's still a debug build, with dev-mode JavaScript and dev tooling, so it doesn't reproduce release-only bugs or final JavaScript performance. Use it day to day and still check release before shipping.

saying these in an interview costs you the question

  • Release builds are the same app, just faster.
  • You can attach React Native DevTools to the release build.
  • Metro must keep running for a release build on the device.
  • --mode release works without any release signing setup.
  • If it works in debug on a device, it will work in release.