Skip to content

Commit

Permalink
Condense update strategies doc (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanoltman authored Feb 7, 2024
1 parent 6dde88c commit ad3c937
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 104 deletions.
3 changes: 3 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ We do not have plans to support changing native code (e.g. Java/Kotlin on
Android or Objective-C/Swift on iOS), and the tool will warn you if it detects
that you have changed native code as it will not be included in the patch.

Shorebird also cannot change your app's Flutter version in a patch, as the
Flutter engine is native code.

### Does Shorebird support Flutter Web?

Code push isn't needed for Flutter web. When a user opens a web app it downloads
Expand Down
171 changes: 67 additions & 104 deletions docs/update-strategies.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,133 +6,96 @@ description: Ways to use Shorebird to update your app

# ⬆️ Update Strategies

By default, Shorebird checks for and silently installs any updates 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. Updates installed via
Shorebird are then used on next launch of the app.

For most users, we recommend this default behavior as we believe it's generally
best for users to not have to think about whether or not they're running the
latest version of your app – things are just magically up to date.

However, Shorebird also provides you with the ability to control when updates
are applied. Sometimes you may wish to prevent the user from using the app
until they've updated to the latest version. For example, if you've made a
breaking change between your app and your server, or if you app communicates
between peers and you've made a breaking change to the protocol.

For such cases, we've developed
[`package:shorebird_code_push`](https://pub.dev/packages/shorebird_code_push),
which enables programmatic control over the Shorebird updater. You can use this
package to check for updates at a time of your choosing and to prompt the user
to update if you wish.
## 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

You may also wish to _disable_ this default update-on-launch behavior and
instead check for updates only at a time of your choosing.
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 the package documentation for more information and
usage examples.

To disable auto-update behavior, add this line to your `shorebird.yaml` file:
You will also want to disable the default automatic update behavior by adding
this line to your `shorebird.yaml` file:

```yaml
auto_update: false
```
You could imagine wanting to do this if you have a large number of users and
wanted to control the rollout of updates (to only update some accounts at a time
to reduce your server load, or reduce rollout risk, for example). Disabling the
default auto-update behavior would allow you to instead check for and install
updates only when you want to.
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`.

:::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.
Any notification service which uses Dart (e.g. FCM) will also trigger the launch
of the Flutter engine when the notification is delivered. When the Flutter
engine is launched, the default auto-update behavior will run. Thus, when a
notification is received, the app will update if it is not already running.
More reliable however is to use the
[`package:shorebird_code_push`](https://pub.dev/packages/shorebird_code_push)
to trigger the update from within a notification handler.

# Patches vs. Releases

Shorebird can only execute code (and thus run its patch logic) when the app is
running. This means that, when a user downloads and launches your app for the
first time, they will see the unpatched release version that was first published
in the store. If you've published a patch, they will not see any patches until
they have opened the app at least twice. The first launch can check for (and
apply) new patches, but a second launch is required to boot from the patched code.

As discussed above, 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. We do not
yet offer an example of this, but
[intend to add one](https://github.com/shorebirdtech/shorebird/issues/950).
Checking for updates does require a network connection. It would also be
possible for you to only gate users if they've not received a "no new version"
message in within a certain amount of time.

We intend for Shorebird to eventually support releasing to the app stores on
your behalf. At present, `shorebird patch` does not publish your a new "release"
to the app store, so the patch will only be visible to users after they've
opened the app at least twice as discused above.

Shorebird also does not yet automatically support deploying a patch across
multiple versions of your app, although this is possible with some automation.
For example, one could write a shell script which took a given git commit,
checked out your various release branches, cherry-picked that commit onto those
branches, and ran `shorebird patch` for each of those branches. We would like
to [support something like this](https://github.com/shorebirdtech/shorebird/issues/860)
out of the box for you in the future, but do not yet.
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 which are already enforcing users are always 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.
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 Play Store updates (e.g.
a few hundred bytes, or a few kilobytes), so it is likely better for your users
(saves them data) if you can use Shorebird to deliver patches instead of
`in_app_update`. However, there are changes which Shorebird cannot make, such as
changes to "native" code (Java, Kotlin, Swift, ObjC), asset files (fonts,
images, etc.) or changes to the Flutter engine itself, so `in_app_update` may
be the best solution in some cases. Again, you will want to write some code to
coordinate between Shorebird and your existing update system to make this
determination.
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](faq#what-types-of-changes-does-shorebird-code-push-support), 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`
https://pub.dev/documentation/shorebird_code_push/latest/shorebird_code_push_io/ShorebirdCodePush/currentPatchNumber.html

We would like to move to a world where Shorebird is push-to-deploy and you don't
have to think about the difference between a patch and a release, but we're
likely still several months away from such a world.

Shorebird also currently makes the guarantee that we do not see or store
your code. Implementing "push to deploy" may not be possible without source code
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
[currentPatchNumber]
(https://pub.dev/documentation/shorebird_code_push/latest/shorebird_code_push/ShorebirdCodePush/currentPatchNumber.html)
method.

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

# Do you need to to make a release to change your version of Flutter?

Yes. Shorebird is only capable of patching Dart code, not native code. So
new versions of the Flutter engine (which is written in C++) cannot be patched
via `shorebird patch`.

0 comments on commit ad3c937

Please sign in to comment.