# Build number is present and incremented for each release

- **Pattern:** `ab-001953` (`mobile-store-readiness.version-management.build-number-incremented`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001953
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001953)

## Why it matters

Both Apple and Google reject any binary whose build identifier is missing, non-integer, or less-than-or-equal to a previously shipped build for the same version string — the upload fails with `ITMS-90062` on iOS or a duplicate-versionCode error on Play Console, blocking the release outright. Without a monotonically incrementing build number, TestFlight cannot route the correct artifact to internal testers, Play Console staged rollouts lose their ordering, and crash reports merge across builds so regressions become untraceable. This is an operational tripwire that delays launches by hours every time it fires.

## Severity rationale

Low at the code level because fixes are mechanical, but each missed increment costs a full submission round-trip.

## Remediation

Declare `ios.buildNumber` as a string and `android.versionCode` as an integer in `app.json`, then automate monotonic increments in CI so human error never ships a duplicate. Store the last-shipped build in a lockfile or release-notes table and fail the build if the new value does not exceed it.

```json
// app.json
{
  "expo": {
    "version": "1.0.3",
    "ios": { "buildNumber": "42" },
    "android": { "versionCode": 42 }
  }
}
```

## Detection

- **ID:** `build-number-incremented`
- **Severity:** `low`
- **What to look for:** Check `app.json` for `buildNumber` (or `ios.buildNumber`, `android.versionCode`). Count all build number declarations found across config files. The build number should be an integer that increments with each build or release. Verify it is higher than the previous release (if known). For Android, `versionCode` must be an integer; for iOS, `buildNumber` can be any string but integers are standard.
- **Pass criteria:** Build number exists and is a positive integer (at least 1). If there is a history of releases, current build number is higher than previous (indicating new build). Both iOS `buildNumber` and Android `versionCode` must be consistent.
- **Fail criteria:** Build number is missing, is not a positive integer (e.g., `1.0` instead of `1`, or `0`), or is lower than a known previous build.
- **Skip (N/A) when:** First release of the app (build number can start at `1`).
- **Detail on fail:** `"buildNumber is missing from app.json"` or `"buildNumber is '1' but previous release was '5' — should increment"`
- **Remediation:** Build numbers allow app stores to sequence releases. Implement:
  1. In app.json:
     ```json
     "version": "1.0.3",
     "buildNumber": "1"
     ```
     Or separately:
     ```json
     "ios": { "buildNumber": "1" },
     "android": { "versionCode": 1 }
     ```
  2. Increment build number for each build (even if version number stays the same):
     - Version: `1.0.0`, Build: `1` (first release)
     - Version: `1.0.0`, Build: `2` (second build of 1.0.0, e.g., if first was rejected)
     - Version: `1.1.0`, Build: `3` (new feature release)
  3. For CI/CD, automate incrementing:
     ```bash
     # Increment build number
     ./increment-build.sh
     ```
  4. Never decrement build numbers — stores reject lower build numbers

Taxons: operational-readiness

HTML version: https://auditbuffet.com/patterns/ab-001953
