In a React Native app's android/app/build.gradle, how do versionCode and versionName differ, and which must change on every Play upload?
answer
- one for machines, one for humans
- integer vs free-form string
- defaultConfig in android/app/build.gradle
- reused code is rejected
- Platform.Version is the OS
basics
~20 sversionCode is an integer Android and Google Play compare to order builds; it must be new and higher on every upload. versionName is a free-form string shown to users and never compared. Both live in defaultConfig in android/app/build.gradle.
solid answer
~40 sBoth live in `defaultConfig` in `android/app/build.gradle`; a fresh React Native project starts at `versionCode 1` and `versionName "1.0"`. **`versionCode`** is a positive integer the system uses to order builds: a device refuses to install a lower code over a higher one, and Google Play rejects an upload whose `versionCode` has already been used, even for a build that was never released. So it must increase on every upload. **`versionName`** is a display string, such as `1.4.0`, shown to users in the store listing and app settings; nothing compares it. A rejected build that you fix and re-upload therefore needs a new `versionCode` even if the `versionName` stays the same. From JavaScript, note that `Platform.Version` is the Android API level, not either of these values.
go deeper
Remember where both live (defaultConfig in android/app/build.gradle), that versionCode is an integer that must go up on every upload, and that versionName is just the label users see.
Explain why Play rejects a reused versionCode, why the OS blocks downgrades, and why split APKs need distinct codes while sharing one versionName.
Design a numbering scheme that survives a team and a pipeline: a monotonically increasing versionCode from a single source, headroom for hotfixes or ABI digits, and a versionName aligned with iOS.
Treat version numbers as a release-process contract across platforms and channels, deciding who owns the source of truth and how it stays unique across branches and parallel builds.
## Two numbers, two audiences Every Android build of a React Native app carries two version values, declared in the `defaultConfig` block of `android/app/build.gradle`: | | `versionCode` | `versionName` | |---|---|---| | Type | Positive integer | Free-form string | | Audience | The OS and Google Play | People | | Compared? | Yes, higher means newer | No | | Must change per upload? | Yes, every upload needs an unused, higher value | No, only when you want users to see a new version | | Fresh template value | `1` | `"1.0"` | The rule of thumb: **`versionCode` is for machines, `versionName` is for humans.** ## What `versionCode` controls `versionCode` is the value the platform uses to decide which build is newer: - **Updates.** Android refuses to install a build with a lower `versionCode` over one with a higher code, so a newer build must carry a higher number. - **Uploads.** Google Play rejects an upload that reuses a `versionCode` already uploaded for the app, including a build you uploaded and then discarded. - **Multiple artifacts.** If you publish ABI-split APKs, the React Native docs point out that each split needs a distinct version code, so schemes often encode the ABI in the low digits. ## What `versionName` controls `versionName` is purely descriptive. It is the string users see, such as `2.3.1`, and it is where a team usually applies semantic versioning. Android does not parse or compare it, so a `versionName` of `1.0.0` after `1.0.1` breaks nothing technically; it only confuses people. ## The scenario: a first Play release that bounces A language-learning app prepares its first Play upload: 1. The team builds `versionCode 1`, `versionName "1.0.0"` and uploads the AAB. 2. A pre-launch check finds the release crashes on older devices, so they discard it and fix the bug. 3. They rebuild without touching `build.gradle` and upload again. Play rejects it: version code 1 has already been used. 4. They bump to `versionCode 2` and keep `versionName "1.0.0"`, because users have never seen a 1.0.0 build. The lesson is that `versionCode` counts **uploads**, while `versionName` counts **releases people see**. ## Managing the numbers in a React Native project Bumping by hand in `build.gradle` works for a single developer but breaks down with a team or a pipeline: - **Derive `versionCode` from a monotonically increasing source**, such as a build number from your pipeline, so two branches never produce the same code. (Wiring that into CI is its own topic.) - **Keep `versionName` in step with the product version** your team announces, and consider keeping it aligned with the iOS marketing version so support tickets are unambiguous. - **A JavaScript-only change still needs a new `versionCode`** when you ship it as a new store binary, because Play sees a new upload either way. - **Leave headroom.** A scheme that multiplies by 10 or 100 to encode the ABI or a hotfix digit needs to stay within the integer range Play accepts. ## A worked numbering scheme One scheme that holds up for a small team shipping a single AAB to Play: - `versionName` follows the product version, for example `1.4.2`. - `versionCode` comes from one counter that only ever increases, such as the pipeline's build number, and is never computed from `versionName`. - A hotfix on an older branch still takes the next counter value, so it is higher than anything uploaded before it. Schemes that derive `versionCode` from `versionName` (for example `major * 10000 + minor * 100 + patch`) look tidy but fail the moment two builds of the same `versionName` must be uploaded, which is exactly the rejected-upload case above. ## Reading the version at runtime A frequent confusion is `Platform.Version`. In React Native it returns the **OS version**: on Android, the API level as a number; on iOS, a version string. It has nothing to do with your app's `versionCode` or `versionName`. Reading the app's own version from JavaScript needs a native module that exposes the package info, typically a community or Expo library. ## Why interviewers ask this The question sorts candidates who have shipped from those who have only run debug builds. Someone who has shipped remembers the rejected upload, knows that a code can never be reused, and can explain why the display string is free to follow marketing while the integer must only ever go up.
- In React Native, what does Platform.Version return on Android, and why is it not the app version?`Platform.Version` returns the operating system version: on Android the API level as a number, on iOS a version string. It describes the device, not your app. The app's `versionCode` and `versionName` come from the installed package, so reading them in JavaScript needs a native module that exposes package info.
- Why do ABI-split APKs of a React Native app need different versionCode values?Each split APK is a separate artifact for the same app, and the store must tell them apart and order them. The React Native docs point to the Android guidance on configuring distinct version codes per split, which is why teams encode the ABI into the low digits of `versionCode` while keeping one shared `versionName`.
saying these in an interview costs you the question
- Bumping versionName alone is enough for Play to accept a new upload.
- versionCode can be reused if the earlier upload was never released.
- Android compares versionName strings to decide whether an update is newer.
- Platform.Version returns the app's versionName.
- A JS-only change never needs a new versionCode, even in a new binary.