Release Architecture

Expo release work has three separate paths: build, submit, and update.

flowchart TD
    code["Code change"] --> nativeQuestion{"Native runtime changes?"}
    nativeQuestion -->|Yes| build["EAS Build"]
    nativeQuestion -->|No| updateQuestion{"OTA safe and compatible?"}
    updateQuestion -->|Yes| ota["EAS Update"]
    updateQuestion -->|No| build
    build --> submit["EAS Submit"]
    submit --> storeReview["Store review or TestFlight"]
    ota --> rollout["Branch/channel rollout"]
    rollout --> rollback["Rollback if needed"]

The Pieces

PieceWhat it doesCommon mistake
EAS BuildCreates signed Android/iOS binariesTreating a dev server run as release proof
EAS SubmitUploads binaries to Play Console or App Store ConnectAssuming submit handles metadata, screenshots, privacy, or review
EAS UpdatePublishes compatible JavaScript/assets OTAShipping native or permission changes OTA
Runtime versionCompatibility boundary between binary and updateReusing one runtime after native changes
ChannelBuild-time pointer to update streamConfusing store tracks with update channels
BranchUpdate stream where update groups are publishedPublishing production fixes to the wrong branch
Store track/TestFlightStore distribution surfaceTreating TestFlight/internal as production

Default Profile Ladder

{
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal",
      "channel": "preview",
      "android": { "buildType": "apk" }
    },
    "production": {
      "channel": "production",
      "autoIncrement": true,
      "android": { "buildType": "app-bundle" }
    }
  },
  "submit": {
    "production": {
      "android": { "track": "production" },
      "ios": {}
    }
  }
}

Keep project-specific credential paths out of committed examples unless the files are gitignored and documented.

Release Rule

If the change affects native code, app config, permissions, SDK version, runtime version, package ID, bundle ID, signing, or store policy, it belongs in a new binary first. OTA can follow after the new binary is available.