# Release Channels vs Environments: They're Not the Same Thing

> How to model OTA release tracks, app-defined runtime context, native compatibility, and Bundle Drop infrastructure without collapsing them into one environment label.

Canonical: https://bundledrop.app/resources/release-channels-vs-environments

Published: 2026-07-19

Updated: 2026-11-01

Category: Engineering

Topics: OTA Operations

A release channel and an environment can both carry names such as `development`, `beta`, or `production`. That overlap is convenient, but it does not make them the same concept.

In Bundle Drop, a **channel** is a named OTA release track inside a project. An app checks one channel, an upload targets one channel, and a compatible bundle can be promoted from one channel to another. A customer app **environment** is application-defined runtime context passed during `BundleDrop.init()`. It can describe where or how that app build operates, but it does not select an OTA release by itself and does not grant access to one.

There are two more identities that are often folded into the same word: `runtimeVersion`, which is the per-platform native compatibility boundary, and Bundle Drop's internal platform environment, where `ENV_NAME` distinguishes `PROD` from `STAG`. Keeping all four axes separate makes release behavior easier to predict.

## Four axes, four questions

Use each value to answer one narrow question:

| Axis | Question it answers | Example |
| --- | --- | --- |
| Channel | Which OTA release track does this app process check? | `beta` |
| Runtime version | Which native interface can this bundle safely execute against? | `5.4-ios-2` |
| App environment | What application context does this running build report? | `staging` |
| Bundle Drop platform environment | Which Bundle Drop infrastructure deployment is running? | `PROD` |

These values may correlate in a particular release pipeline, but none should silently substitute for another. A production app environment can check a `beta` channel for an employee dogfood build. A `production` channel can contain compatible bundles for several runtime versions while users adopt a new store binary. Bundle Drop's `STAG` infrastructure can be used to validate platform changes without turning `STAG` into a customer channel or app environment.

## Channels route OTA releases

Every Bundle Drop project starts with a `develop` channel, and teams can create other named tracks. The app chooses its active track at startup:

```ts
BundleDrop.init({
  environment: "staging",
  channelName: "beta",
  policy: "on-next-launch",
});
```

The upload command names the same track explicitly:

```bash
npx bundle-drop upload ios --channel beta
npx bundle-drop upload android --channel beta
```

The channel must already exist. Upload does not create it. When an installation checks for an update, Bundle Drop resolves within that project, channel, platform, and runtime-version lane, then applies publishing, targeting, and rollout rules.

After those identities establish the lane, [managed publishing, targeting, rollout percentage, and fallthrough](/resources/staged-rollouts-targeting-rollback) determine which release wins inside it.

This makes a channel a release-routing mechanism. Moving an app process from `beta` to `production` changes the release track it checks. Changing its app environment from `staging` to `production` does not perform that switch.

The full lifecycle is documented under [Channels](/docs/channels), and the supported upload inputs are listed under [Uploading](/docs/uploading).

## App environments describe application context

The `environment` option passed to [`BundleDrop.init()`](/docs/init-update-manager) is required customer-app context. Teams define its vocabulary. Common values are `development`, `staging`, and `production`, but a product might instead use `internal`, `preview`, or region-specific labels.

Within the product model, this is the `appEnvironment` concept. Some compatibility API payloads and storage records still use the field name `environment`; that naming does not turn it into infrastructure configuration. Bundle Drop records the value with update checks, install reporting, and analytics so activity can be understood in the app's own context.

It is not an authorization boundary. A device does not gain permission to an OTA bundle because it reports `environment: "production"`, and changing the string is not a safe way to protect privileged code or data. Product authorization remains the responsibility of the application and its backend. If a release needs audience rules, use defined user properties and the documented [targeted rollout properties](/docs/targeted-rollout-properties), while remembering that rollout targeting is still release eligibility rather than product authorization.

Treat app-environment values as durable telemetry dimensions. Keep spelling and casing consistent across builds; otherwise `production`, `prod`, and `Production` become separate labels that make reporting harder to interpret.

## Runtime versions enforce compatibility

`runtimeVersion` is independent of both channel and environment. It identifies the native capability set available to an OTA bundle on a specific platform. Bundle Drop delivers a bundle only when its platform runtime version exactly matches the installed binary's runtime version.

For example, in the default manual compatibility mode:

```js
module.exports = {
  defaultChannel: "production",
  runtimeVersion: {
    ios: "5.4-ios-2",
    android: "5.4-android-1",
  },
};
```

An iOS bundle for `5.4-ios-2` can be present on `develop`, `beta`, and `production`. Promotion changes where the artifact is available; it does not change the artifact's runtime compatibility. Likewise, renaming an app environment cannot make a bundle compatible with a different native binary.

Opt-in native fingerprinting derives that identity from collected native inputs instead of relying only on literals. Hybrid compatibility also includes an explicit platform boundary in the derived identity. Neither changes what a channel or app environment means: promotion cannot rewrite the target's fingerprint or cross an incompatible runtime line.

For the full per-platform compatibility-line model, see [Runtime versions: the compatibility boundary for React Native OTA updates](/resources/runtime-versions-compatibility-boundary). To choose manual, fingerprint, or hybrid control, use the [compatibility strategy guide](/resources/native-fingerprints-runtime-versions). The configuration reference lives in [Runtime Version](/docs/runtime-version).

## `PROD` and `STAG` belong to Bundle Drop infrastructure

Bundle Drop itself has platform and administration deployments. Their `ENV_NAME` values are normalized around `PROD` and `STAG`. This setting controls which Bundle Drop infrastructure deployment a service or administration surface belongs to.

It is not a customer-controlled release track. Do not create logic that maps `ENV_NAME=PROD` to the customer's `production` channel, or normalize a customer's app-defined `staging` value into `STAG`. Those strings live in different namespaces and have different owners:

- platform operators choose `PROD` or `STAG` for Bundle Drop services;
- application teams define their own app-environment values;
- project maintainers create channels inside a project;
- native release engineers define runtime versions per platform.

This boundary matters in tooling and documentation. A dashboard connected to Bundle Drop `STAG` may still manage a project containing a channel literally named `production`. The identical-looking label describes customer release intent, not the infrastructure serving the dashboard.

## Model development, beta, and production explicitly

A useful release setup models each concern on its own, even when the names happen to align.

| Build or audience | App environment | Channel | Runtime version |
| --- | --- | --- | --- |
| Local developer build | `development` | `develop` | platform-specific dev line |
| Employee or QA build | `staging` | `beta` | same as production only if natively compatible |
| Store build | `production` | `production` | current iOS or Android production line |

This table is a convention, not a Bundle Drop requirement. Consider a store-signed employee build that uses real production APIs but should receive updates before the public. Its values could be:

```ts
BundleDrop.init({
  environment: "production",
  channelName: "beta",
  policy: "on-next-launch",
});
```

That combination is coherent: the app operates in the product's production context while following the beta OTA track. If it is built from the same native inputs as the public app, both can also share a runtime version. If the employee build includes an extra native diagnostics module, it needs a separate runtime compatibility line even if both builds call the same production APIs.

Another team may use one app environment and several channels, or several app environments that all check one channel. Choose combinations deliberately rather than enforcing a one-to-one mapping that the runtime does not require.

## Promote artifacts, not assumptions

Channels support a verification pipeline without rebuilding or re-uploading identical content. A typical flow is:

1. Upload an iOS or Android bundle to `develop`.
2. Validate that exact hash on compatible internal installations.
3. Promote the bundle to `beta` for a wider test group.
4. Promote the same bundle to `production` when it passes release checks.

Promotion adds the existing bundle to the target channel as-is. Its hash, platform, runtime version, and bundle version remain unchanged. The source channel retains it, and rollback or removal is channel-scoped. On the target channel, version ordering still matters: an artifact older than the target lane's latest bundle is added but does not automatically become the served update.

A channel designated as the project's production channel also has production retention behavior. That designation affects how long newly uploaded or promoted bundles are retained; it does not convert the channel into an app environment. See [Production Channels](/docs/production-channels) for those lifecycle rules.

Promotion works best when the pipeline preserves identity. Validate the exact hash, promote it forward, and avoid rebuilding nominally identical source at each stage. Rebuilding creates another artifact and weakens the evidence gathered in the previous channel.

## Avoid overloaded naming and hidden coupling

Several patterns create ambiguity:

- **Using runtime versions as stages.** Values such as `beta` and `production` do not describe native compatibility. They force unrelated compatibility changes into release routing.
- **Using app environment to choose updates implicitly.** The update track is `channelName`. Keep that choice visible in startup configuration or an intentional channel-switching flow.
- **Treating a production channel as authorization.** Anyone who can legitimately run a configured client still needs application-level permissions for protected capabilities and data.
- **Mapping customer values to `PROD` and `STAG`.** Internal platform environment normalization must not rewrite customer app context.
- **Creating a channel per native build.** A channel can hold multiple runtime lines. Create a new channel for a distinct release audience or workflow, not every runtime bump.
- **Encoding every user segment as a channel.** If users share a release track but a rollout needs a temporary audience gate, typed rollout properties are usually the clearer mechanism.

Prefer short, stable, lowercase channel names such as `develop`, `beta`, and `production`. Document what audience each channel serves, who may publish or promote into it, and whether managed publishing or production retention is enabled. Separately document the app-environment vocabulary and which backend configuration each value selects.

## Review releases as a tuple

Before publishing, review the complete release identity rather than asking whether a bundle is "for production":

```text
project + channel + platform + runtimeVersion + bundle hash
```

Then record the app environment as execution context and verify any rollout eligibility rules separately. A release checklist should answer:

1. Which channel will installations check?
2. Which iOS or Android runtime line can execute the artifact?
3. Which exact bundle hash was validated and promoted?
4. What does the app-defined environment mean for API configuration and telemetry?
5. Are audience controls release targeting, or are they product permissions that belong elsewhere?

Once those answers are explicit, familiar labels stop carrying hidden behavior. Channels route releases, runtime versions protect native compatibility, app environments describe the customer's runtime context, and `PROD` or `STAG` identifies Bundle Drop's own infrastructure. The names may resemble one another; the contracts should not.
