Why Versioning Matters
Every app update submitted to the App Store or Google Play needs a version identifier. Versioning affects store listings, user expectations, update prompts, crash reporting, analytics, and your ability to force-update or roll back. Getting it right from the start saves confusion and technical debt later.
Version Number vs. Build Number
Both platforms use a two-part identification system:
iOS
- CFBundleShortVersionString (Version): The marketing version users see (e.g., "2.5.0"). Must increase with each App Store release.
- CFBundleVersion (Build): A unique number for each build (e.g., "147"). Must increase for each upload to App Store Connect, including TestFlight builds.
Android
- versionName: The user-facing version string (e.g., "2.5.0"). Displayed on Google Play.
- versionCode: An integer that must strictly increase with each upload (e.g., 47). Never displayed to users but used by Google Play to determine update order.
The version number communicates meaning to users. The build number is a technical identifier for the specific binary.
Semantic Versioning (SemVer)
The most widely adopted versioning scheme: MAJOR.MINOR.PATCH
- MAJOR (2.0.0): Breaking changes, major redesigns, significant new features. Users expect a noticeably different experience.
- MINOR (2.5.0): New features, non-breaking improvements. Users notice new functionality.
- PATCH (2.5.1): Bug fixes, performance improvements. Users may not notice the change.
Practical Application
For mobile apps, strict SemVer is often relaxed:
- Many apps use MAJOR.MINOR format publicly (v2.5) and track PATCH internally
- MAJOR bumps are often marketing-driven ("we redesigned the app")
- MINOR bumps correspond to feature releases
- PATCH bumps are for hotfixes between feature releases
Build Number Strategies
Sequential Integer
The simplest approach. Start at 1 and increment by 1 for every build:
- Build 1, 2, 3, ... 147, 148
- Easy to track, unambiguous ordering
- Works well with CI/CD (use the CI build number)
Date-Based
Encode the date into the build number:
- Format: YYYYMMDDNN (e.g., 2026032201 for the first build on March 22, 2026)
- Self-documenting (you can tell when a build was created from its number)
- NN suffix allows multiple builds per day
CI Run Number
Use your CI system's run counter directly as the build number:
- GitHub Actions: The workflow run number
- Bitrise: BITRISE_BUILD_NUMBER
- Consistent, automatic, no manual management
The key rule for both platforms: the build number / versionCode must always increase. You can never submit a build with a lower number than a previously submitted build.
Version Management in Cross-Platform Projects
React Native (Expo)
In app.json or app.config.js:
- version: Maps to CFBundleShortVersionString (iOS) and versionName (Android)
- ios.buildNumber: Maps to CFBundleVersion
- android.versionCode: Maps to versionCode
EAS Build can auto-increment build numbers using the --auto-submit flag or remote version source.
Flutter
In pubspec.yaml:
- version: 2.5.0+47 where 2.5.0 is the version name and 47 is the build number
- The build number maps to both CFBundleVersion (iOS) and versionCode (Android)
React Native CLI
Version numbers are set in ios/[project]/Info.plist and android/app/build.gradle. Many teams use react-native-version or fastlane to automate synchronization.
Force Update and Minimum Version
For critical security fixes or breaking API changes, you may need to force users to update:
Strategies
- Firebase Remote Config: Store the minimum required version on your server. On launch, compare the app's version. If it is below the minimum, show a full-screen prompt.
- Custom API endpoint: Return the minimum version from your own backend.
- Apple/Google APIs: Both platforms offer update prompt APIs, but they only suggest updates and cannot force them.
Implementation Tips
- Compare version numbers using proper semantic comparison (not string comparison)
- Allow a grace period before enforcing the update
- Provide a clear message explaining why the update is required
- Always have a fallback for users who cannot update (old devices, OS version)
Changelog and Release Notes
- Write release notes from the user's perspective, not the developer's
- Group changes by category (new features, improvements, fixes)
- Keep it brief for minor updates, more detailed for major versions
- Both stores allow per-version release notes
Best Practices
- Decide on a versioning strategy at the start of the project and document it
- Automate version and build number management through CI/CD
- Never manually set build numbers. Let CI handle it.
- Tag Git commits with the version number for traceability
- Keep iOS and Android version numbers in sync unless there is a strong reason not to
- Use a changelog file (CHANGELOG.md) in your repository to track what changed in each version