---
title: "Update Strategies"
description: "Choose when your app checks for and applies patches: automatically on launch, from your own code, or after a push notification."
---

## Automatic (default)

By default, Shorebird checks for and installs new patches in the background on launch. It does this via a background thread to ensure that it does not affect the launch speed of your application. Patches installed via Shorebird are then available on next launch of the app.

## Manual

Shorebird also provides you with the ability to control when patches are applied. You may want finer-grained control over updates to:

* Control rollout of patches (to only update some accounts at a time to reduce your server load or reduce rollout risk, for example).
* Require users to update to the latest version before they can use the app (see note below).

To manually manage updates, you can use the [`package:shorebird_code_push`](https://pub.dev/packages/shorebird_code_push), which enables programmatic control over when the Shorebird updater checks for and downloads patches. See [Check for patches from your code](/code-push/how-devices-get-patches/#check-for-patches-from-your-code) for an example and what each call returns and throws.

You will also want to disable the default automatic update behavior by adding this line to your [`shorebird.yaml`](/code-push/shorebird-yaml/#auto_update) file:

```
auto_update: false
```

Disabling automatic updates is not required to use `shorebird_code_push`, but Shorebird will automatically download and apply updates if your shorebird.yaml file does not contain `auto_update: false`.

Turning off automatic updates limits rollback

Devices learn that a patch was [rolled back](/code-push/rollback) when they check for updates. With `auto_update: false`, that check runs only when your code calls `checkForUpdate()` or `update()`. If a patch breaks your app before that call, for example by throwing during startup, rolling the patch back can’t reach those devices, and the only way you can replace the patch is a new store release. Shorebird’s automatic rollback doesn’t help here: it only catches patches that fail to load, not ones that throw while running.

If you turn off automatic updates, check for updates as early in startup as you can, and keep that code simple so patches are unlikely to change it.

Note

Because Shorebird can only download and apply patches when your app is running, the user will see the unpatched release version that was first published in the store when they first launch your app. If it is important to your business to gate usage of your app on users having the latest code, it could be appropriate for you to check for updates as part of a login screen, or other launch gate.

## Triggering updates via notification

It is possible to trigger updates via push notifications.

Shorebird does not provide its own notification service, but it’s possible to use others, such as [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging) to send a notification to your app, and then use that notification to trigger an update. Because these services typically allow you to target specific devices, you could use this to trigger updates for specific users.

Because any notification service which uses Dart (e.g. FCM) will also trigger the launch of the Flutter engine when the notification is delivered, the app will update if it is not already running.

If you are manually managing updates with the `shorebird_code_push` package, you can check for and trigger updates in your notification handler.

## How should Shorebird interact with other update systems (e.g. `in_app_update`)?

For applications that already ensure users are on the latest version (e.g. with `in_app_update`, a system on Android whereby the Play Store will automatically prompt users to update your app), you will likely want to write some code to coordinate between Shorebird and your existing update system.

[`package:shorebird_code_push`](https://pub.dev/packages/shorebird_code_push) can help you here. For example, with `in_app_update`, you could use `package:shorebird_code_push` to check if the user has already applied the necessary patch and *not* then prompt them to update.

Shorebird patches are typically much smaller than full app downloads (a few KB on Android, a few hundred KB on iOS), so it is likely better for your users if you can use Shorebird to deliver patches instead of `in_app_update`. However, there are [changes which Shorebird cannot make](/code-push/#what-types-of-changes-can-be-included-in-a-patch), so `in_app_update` may be the best solution in some cases.

Shorebird “patches” also do not change the version number of your app, so `in_app_update` will not see them as a new version. This is by design – patches are applied to releases, rather than being new releases themselves. This can complicate your analytics/reporting code as you will have the case where e.g. `1.0.1+13, patch 1` has identical dart code to `1.0.1+13, no patches`. You can get the current booted patch number via `package:shorebird_code_push`’s [readCurrentPatch](https://pub.dev/documentation/shorebird_code_push/latest/shorebird_code_push/ShorebirdUpdater/readCurrentPatch.html) method.

Shorebird also currently makes the guarantee that it does not see or store your code. Implementing “push to deploy” may not be possible without source code access, which is not a change Shorebird would make lightly.

## Can you update your app without starting it twice?

The short answer is no: a patch runs from the next start after it downloads, whatever your update configuration. You can restart the app for the user (see [Restarting after an update](#restarting-after-an-update)).

There are two steps required to apply an update:

1. **Download and Install**: The Shorebird updater needs to fetch the patch from the CDN and write/apply the binary diff to the local cache directory on disk.
2. **Launch with New Code**: The app needs to restart so that the modified Flutter engine loads the newly cached patch files instead of the base release files.

Because the default Shorebird configuration performs the update check and download **in the background on startup**, the download completes *after* the Dart VM is already running the base code. Consequently, the patch cannot be hot-swapped into memory dynamically on that launch.

During development or QA testing, this means you must restart the app twice:

* **First Launch**: App launches (unpatched), detects the patch, downloads it in the background, and installs it.
* **Second Launch**: App launches, detects the installed patch from the local cache, boots from it, and displays the changes.

Managing updates manually doesn’t change this. A patch that `package:shorebird_code_push` downloads still takes effect only the next time the app starts, never on the launch that downloaded it. What manual updates let you control is when the check and download happen: by default Shorebird checks only on launch, but you can check at any time, or on other signals that suit your app (such as a notification, see above). To have users run a downloaded patch sooner, restart the app as described in [Restarting after an update](#restarting-after-an-update).

While the Dart VM has the ability to hot-restart, most apps use plugins or call out to other native code which would not be aware that the Dart portions have restarted and are likely to end up in a state that could lead to a crash in the native side of the application. A “hot-restart” feature might be worth exploring in the future, in cases where the developer can ensure that the native side of the app is in a safe state to be restarted, or where a set of plugins that are safe during a hot-restart could be provided.

Shorebird’s default settings are such that the user should never have to think about an update. If they’re regularly using the app, it will just be always up-to-date. (Shorebird will detect and install updates when they launch the app while connected to the internet.) Modern mobile operating systems are very aggressive about killing background apps, so the app will be restarted from scratch fairly often even if it doesn’t appear to the user as a full restart.

## Restarting after an update

If you want users to run a downloaded patch without waiting for their next launch, restart the app once the download finishes. Shorebird doesn’t restart your app for you; use a package such as [`terminate_restart`](https://pub.dev/packages/terminate_restart) or your own restart mechanism.

`checkForUpdate()` returns `UpdateStatus.restartRequired` when the patch on disk differs from the one the app is running. That happens after a new patch downloads, and also after the running patch is [rolled back](/code-push/rollback), when a restart returns the app to the release or an earlier patch. Rollbacks report `restartRequired` with `shorebird_code_push` 2.0.6 and newer, on releases built with Flutter 3.41.9 and newer.

Restart safely:

* Restart at most once for each downloaded patch. Record which patch you restarted for, and don’t restart again for the same one, so a status that stays `restartRequired` can’t put the app in a restart loop.
* Don’t restart in the middle of what the user is doing. Ask first (for example, a “Restart to update” prompt), or wait for a natural moment such as the app going to the background. Keep an immediate restart for urgent fixes, such as a crash or security issue.

```
import 'package:shorebird_code_push/shorebird_code_push.dart';

final updater = ShorebirdUpdater();

// The patch the app last restarted for (0 means the release itself). Persist
// it, for example with shared_preferences, so it survives the restart.
int? lastRestartedFor;

Future<void> maybeOfferRestart() async {
  final status = await updater.checkForUpdate();
  if (status == UpdateStatus.outdated) {
    await updater.update(); // Throws UpdateException if the update fails.
  } else if (status != UpdateStatus.restartRequired) {
    return;
  }

  // Nothing new to run yet, for example while a download is still in progress.
  final current = (await updater.readCurrentPatch())?.number ?? 0;
  final next = (await updater.readNextPatch())?.number ?? 0;
  if (next == current || next == lastRestartedFor) return;

  // Ask the user. If they agree, save `next` as lastRestartedFor, then restart.
}
```
