How staged rollouts, targeting, and rollback fit together
A resolve-path view of managed publishing, audience rules, stable rollout buckets, fallthrough, and recovery from an unhealthy update.
A safe OTA release is not a single percentage slider. It is a decision system with separate controls for publication, audience selection, gradual exposure, and recovery. Those controls must compose predictably when a channel contains several bundles and devices report different runtime versions, properties, and installed state.
In Bundle Drop, the server makes the release decision on every update check. The app identifies its project, channel, platform, runtime version, install, current bundle, and locally rejected bundle hashes. The server then selects an eligible target; the client decides when to download and apply it according to its update policy. Keeping those responsibilities separate makes the behavior testable.
Release Channels vs Environments explains the identities that establish the release lane before the selection rules in this article run.
Managed publishing establishes the gate
Without managed publishing, the newest compatible, non-revoked bundle on a channel is eligible. Uploading can therefore change what devices receive as soon as the bundle is available.
With managed publishing enabled, upload and eligibility become different events. Only bundles represented by a published rollout in the same channel, platform, and runtime-version lane can be selected. A rollout that is paused or has not started does not make its bundle eligible. This is why managed publishing must be enabled before staged rollout percentage is expected to control delivery.
The distinction is operationally important:
- Upload creates and validates an artifact on a channel.
- Publish makes a rollout participate in update resolution.
- Pause removes that rollout from active selection without changing the artifact.
- Complete leaves the release fully published for its lane.
- Revoke or rollback changes which target is safe to serve.
Treat managed publishing as the production release boundary. A CI job can upload an artifact, complete verification, and leave the decision to expose it as an explicit dashboard action.
Resolution is newest-first, with fallthrough
For a device’s channel, platform, and exact runtime version, published rollouts are evaluated from the highest bundle version downward. The first rollout for which the install passes every active gate wins.
Resolve eligibility in a fixed order
Gate 1
Managed publishing
Only published rollouts enter eligibility evaluation.
Gate 2
Newest rollout first
Candidates are checked from the highest bundle version downward.
Gate 3
Target rules
Any configured property rule may qualify the current install.
Gate 4
Stable bucket
The install-derived bucket is compared with the rollout percentage.
Eligible
Select this bundle and return its compatible delivery path.
Not eligible
Continue to an older published rollout instead of ending the resolve.
The sequence is:
- Ignore revoked bundles and hashes the device reports as locally rejected.
- Consider the newest published compatible rollout.
- If targeted rollout is enabled, evaluate its property rules.
- If targeting matches, compare the install’s stable bucket with the rollout percentage.
- Select the bundle when both gates pass; otherwise continue to the next older published rollout.
- Return no update when no candidate is eligible.
Fallthrough is what allows layered releases. Consider three published bundles on one runtime line:
| Bundle | Audience | Percentage | Intended role |
|---|---|---|---|
| v43 | isBetaUser = true | 20% | New feature canary |
| v42 | country = "US" | 100% | Region-specific fix |
| v41 | No targeting | 100% | General stable release |
A beta user in the v43 cohort receives v43. A beta user outside its percentage can still be considered for v42, then v41. A non-beta US user skips v43 and receives v42. A user matching neither targeted release receives v41.
This is not “assign a user permanently to one bundle.” It is deterministic evaluation of the current ordered release set. Publishing a newer rollout can change the result; increasing a rollout percentage can admit more installs; pausing or revoking a rollout removes it from consideration.
Targeting narrows an audience
Targeted rollout properties add an audience gate before percentage selection. Apps set typed string, finite-number, or boolean values such as tier, country, or isBetaUser. Property definitions are created in the dashboard, and matching keys and values are case-sensitive.
Multiple target values inside a rule mean “match any listed value.” Multiple rules on a rollout also use OR logic. With these rules:
tier -> ["premium", "gold"]
country -> ["US"]
an install matches if its tier is premium or gold, or its country is US. It does not need to satisfy both property keys. This detail should be visible in release review because engineers often assume separate filters are ANDed.
Targeting does not replace runtime compatibility, managed publishing, or staged rollout. It only narrows the group to which the percentage gate applies. A rollout at 20% with targeting enabled means roughly 20% of matching installs, not 20% of the entire channel.
Properties reflect the current device state reported during update checks and install reports. If a subscription tier, beta flag, or region changes, the next resolution can produce a different eligible release. Do not use rollout properties as an authorization boundary: code and assets delivered to devices should not contain secrets, and product permissions still belong in the application and backend.
Percentage rollout uses a stable install bucket
Each installation receives a deterministic bucket derived from its install ID. A 10% rollout admits the lowest tenth of the bucket range; raising it to 25% admits that original group plus the next slice.
This has three useful properties:
- the same installation does not get redrawn on every check;
- increasing the percentage only adds installations;
- an installation that qualified at 10% remains qualified at 25% and 100%.
The bucket is derived from install identity alone, not independently per release. The same low-bucket installations tend to enter the earliest slice of successive rollouts. That makes cohorts predictable, but it also means an early cohort can see canaries more frequently than the rest of the population. Account for that in product support and test-device planning.
A stable bucket controls selection, not immediate adoption. Devices must still launch, check, download, and apply according to policy. Dashboard delivery curves can therefore lag behind the configured percentage.
Rollout and rollback solve different problems
Staging limits how many eligible installations can acquire a release. Rollback changes the safe target after a release is found to be bad. Both reduce impact, but neither substitutes for the other.
Bundle Drop has two recovery layers:
- Dashboard rollback is channel-scoped and changes server-side release selection. It moves devices toward the previous working OTA bundle, or toward the embedded native bundle when no earlier OTA target exists.
- Automatic crash protection is local to an installation. It protects a device that has already applied an OTA bundle and repeatedly fails before that candidate is confirmed healthy.
Treat a newly applied OTA bundle as a startup candidate
Apply
Candidate bundle
The update becomes active, but is not yet the known-good startup state.
Observe
Launch reaches BundleDrop.init()
Bundle Drop can increment an unconfirmed launch or accept a healthy report.
Healthy
Promote the candidate to the device's known-good bundle.
Unconfirmed limit reached
Reject the failed hash locally and return to the previous verified or embedded bundle.
After an OTA bundle is applied, the SDK treats it as a startup candidate. Each launch that reaches early BundleDrop.init() but does not reach the configured healthy state is unconfirmed. At maxCrashCount, the SDK returns to the last known-good OTA bundle or the embedded bundle and records the failed hash locally. Future checks, downloads, manual installs, and applies reject that hash on the same installation.
This protection has a precise observation boundary: it only observes launches that reach BundleDrop.init(). A native crash, loader failure, or JavaScript crash before initialization cannot increment the local health state. Call initialization as early as the application architecture allows, and use native crash reporting for failures outside that boundary.
By default, a candidate is marked healthy on the next JavaScript tick after initialization. Teams with a reliable readiness milestone can enable manual health reporting and call BundleDrop.reportHealthy(). Manual mode is stricter, but forgetting the report can roll back a healthy update after repeated unconfirmed launches. See Rollback for configuration and defaults.
Design a release ladder
A percentage ladder should be tied to evidence, not elapsed time alone. One practical sequence is:
- Publish to an internal or beta channel and verify the exact bundle hash.
- Promote or publish to a production channel with managed publishing enabled.
- Begin with a small percentage or a deliberately targeted audience.
- Monitor crashes, startup health, update-check failures, download failures, and product-specific guardrails.
- Increase through predetermined steps only when the signals remain within bounds.
- Complete at 100%, or pause and roll back when a stop condition is met.
Define the stop conditions before publishing. “Watch the dashboard” is not a release policy. Name the metrics, observation window, responsible engineer, and decision threshold. Include a route for failures that appear only in a property-targeted segment and a route for devices that cannot reach the update service.
Percentage alone does not validate compatibility. Test every platform/runtime lane separately. An iOS rollout at 100% says nothing about Android, and a release for runtime B cannot repair a failure on runtime A unless a compatible bundle is published there.
Exercise failure modes before production
Use a release candidate that can be identified unambiguously and test at least these scenarios:
- a matching targeted install inside the percentage receives the newest bundle;
- a matching install outside the percentage falls through to an older eligible rollout;
- a non-matching install follows the documented OR rule and falls through;
- an install keeps the same eligibility across repeated checks and percentage increases;
- a paused rollout is not selected;
- a revoked or locally rejected hash is not returned;
- server rollback selects the previous safe target;
- local crash protection restores the previous OTA bundle;
- local crash protection restores the embedded bundle when there is no previous OTA bundle;
- a failure before
BundleDrop.init()appears in native observability even though local health protection cannot see it.
The staged rollout guide documents cohort behavior, while managed publishing and targeted rollout properties cover the two server-side gates.
Keep the model explicit
The safest mental model is a pipeline: compatibility chooses the lane, managed publishing chooses the candidate set, newest-first evaluation chooses priority, targeting narrows the audience, the stable bucket controls exposure, and rollback changes the safe state when evidence shows a failure.
This pipeline selects the release; it does not choose its transport. Why Bundle Drop combines patch delivery with full-bundle reliability explains the separate delivery decision.
When those responsibilities are kept separate, teams can answer why a specific installation received—or did not receive—a bundle. That explainability is as important as gradual delivery itself. It turns rollout from a percentage control into an auditable release-engineering system.