🎯 Ready, Aim, Release: Android app rollout tips
Tags: androiddev, mobile, android
Published: 2024-03-12 23:54:30

*Practical tips and strategies for smooth mobile app releases and post-release issue management*

![](https://cdn-images-1.medium.com/max/2048/1*EcwiJnC_Qf6C3Dd3lh3ZQA.jpeg)

*Note: This post was originally published on [Medium](https://medium.com/@linakis/ready-aim-release-android-app-rollout-tips-1bc6f851e6fb). For the purpose of this blog post, I have created (and [published](https://play.google.com/store/apps/details?id=net.linakis.colorandom)) an open-source toy app on [GitHub](https://github.com/linakis/colorandom). *The app randomly colorises the state of Colorado on a USA map when the user taps on the screen. It’s called Colorandom (pun intended).*

---

There is a wide misconception that once you finish your dev work (add as many pre-release steps as you like here: QA, UAT, etc) you can release your mobile app and all your users will get your shiny new version. This assumption comes from the web realm where once a change is uploaded to your server then all your users will have immediate and exclusive access to your new version. The truth is far from it.

Once your binary is ready you will have to 1) upload it to the store, 2) wait for it to be approved, 3) publish it, and then comes the most important part: 4) users will have to download it. That last part may seem obvious but users not downloading your update timely (or even at all) is very likely. We have noticed that, for our app, **80% of the users update within the first 5 days**, and **2–5% never update** (unless forced to do so). That last stat is pretty much all you need to know. You could think of it like this

> Launching a mobile app is like releasing an arrow - once it’s out there, you can’t call it back. Make sure your aim is true before you let it fly.

Mobile apps are quite different from how web apps work — release a version, and everyone can see it right away. Something broke? Just release a hotfix and you will have zero users affected after that. For mobile developers, once your app is on your customers’ devices, you can’t just take it back, your users will keep crashing until your hotfix app is uploaded to the store and your users download it.

However, there are mechanisms and techniques one can use to get some peace of mind when releasing a mobile application in the Google Play store. In this blog post, we will try to iterate all the things that you should do to make sure that a potential crash or bug would have minimal impact on your user base.

## Release 🚀

When you’re ready to release, do it smartly. **Plan ahead to avoid doing it late in the day or on Fridays**. Nobody wants to deal with unexpected issues after hours due to a poorly timed release.

Luckily Google Play Store offers the option to do a **[staged rollout](https://support.google.com/googleplay/android-developer/answer/6346149?hl=en)**. Using this mechanism you can **gradually release updates** (or new versions) of your app to your end users. Instead of making the update available to all users at once, the rollout is divided into stages, with a percentage of users receiving the update initially. This allows developers to monitor the release in a controlled sample and address issues early on, before reaching all users. **Always opt-in to do a staged rollout instead of releasing to 100%.**

Apple Store offers a similar functionality called [phased releases](https://developer.apple.com/help/app-store-connect/update-your-app/release-a-version-update-in-phases/). One major difference is that Play Store doesn’t have an automated daily increase of the percentage. Instead, the developer has to manually bump the percentage. We have found that it’s a good habit to keep the same percentage per day progression to keep things in sync on both platforms. Even if you don’t have an iOS app that forces you to keep that schedule, it’s still very beneficial to follow the same 7-day progression.

    +-------------+---------------------+
    | Day Release | Percentage of Users |
    +-------------+---------------------+
    | 1           | 1%                  |
    | 2           | 2%                  |
    | 3           | 5%                  |
    | 4           | 10%                 |
    | 5           | 20%                 |
    | 6           | 50%                 |
    | 7           | 100%                |
    +-------------+---------------------+

Some things that you should have in mind when doing a staged rollout:

* New and existing users are randomly selected to receive your update. *Unlike the App Store, where randomness only affects existing users, new users always get the latest version.*

* If you halt and then resume the rollout, the same group of users who were initially selected to receive the update will continue to be the recipients. There won’t be a new selection process.

* If you start a staged rollout for a new version of your app while the rollout of the previous version is still ongoing, the new version will target the same group of users as the previous version. However, the percentage of users who receive the new release may vary depending on the rollout percentage set for the new release.

* During a staged rollout, the update will only be accessible to a certain percentage of users. However, it may take some time for the update to be available to everyone within that group.

* Users who are part of a staged rollout for your app won’t receive notifications informing them of the update. This means they may not be aware that they’ve received a new version of the app.

*More info on the eligibility and targeting criteria in [Play Console help](https://support.google.com/googleplay/android-developer/answer/6346149?hl=en)*

## Monitor 👀

Once the app is released to 1% you must actively monitor Crashlytics (or set [integrations](https://support.google.com/firebase/answer/9005934)) for fresh and regressed issues. Anything that pops up should be dealt with immediately.

![](https://cdn-images-1.medium.com/max/2000/1*AwrUOj_Rr6CQRpXKWlQ43g.png)

Another very useful functionality of Crashlytics is [velocity alerts](https://firebase.google.com/docs/crashlytics/velocity-alerts). These are alerts that notify you when sudden spikes or increases in the crash rate of your app occur. For such an alert to be triggered the following conditions must apply.

Within a one-hour time period:

* the crash should affect more than your defined threshold for crash-free user sessions.

* the app has at least 250 sessions

* there was no alert previously raised for the issue in the app.

**As long as you’re in a staged rollout at any point you have the option to halt the release.** This means that discovering the bug at 1% gives you the time to fix it with 99% of your users not even noticing it. Even if you cannot afford the 7-day rollout plan proposed above and you need it to be out there immediately there is still a way to benefit from this mechanism. Make sure that you do the following:

 1. Release at the beginning of the day

 2. Release to 99% of your users so you are still technically in a staged rollout. *Remember: releasing doesn’t mean all your users will actually update on that day.*

 3. Monitor for crashes/velocity alerts and if all is well then you can release to 100% by the end of the day.

## Halt ✋

So you got a crash that affects “everyone”, no big deal, all you have to do is halt the release. Once you are in a halted release there are 3 distinct segments your users will belong.

 1. **New users** who download your app while it’s halted will always download the previous version.

 2. **Users who are eligible to update but haven’t done so yet** are not technically affected. If they close the Play Store application and re-open it the update option will disappear. We have noticed however that the app is still updateable in the “Pending downloads” section.

 3. **Users who update the app** (manually or [automatically](https://support.google.com/googleplay/answer/113412?hl=en#auto)) are potentially affected. It’s like [Schrödinger’s cat](https://en.wikipedia.org/wiki/Schr%C3%B6dinger%27s_cat) — users aren’t affected until they actually try to use the app. Once they do, they’ll find the bug you tried too hard to avoid.

*Note: This is yet another point where Apple’s phased rollout differs from Google's staged rollout. Apple calls this step “pause” (vs halt) and I believe this is intentional because __when you “pause” the release in iTunes Connect all you do is stop the auto percentage increase__. The new version will still be available for new and existing (eligible) users. This means there is no baked-in rollback for iOS users. Once the user is eligible he will receive the latest version.*

As we said, once the arrow hits the target there is no way back. There is however a way that we can make the arrow disappear. We can remotely disable the offending feature as long as we provision this functionality when we write our code. This practice is generally referred to as [feature toggles](https://martinfowler.com/articles/feature-toggles.html) (aka feature flags).

## Toggle 🚩

Firebase has this great little feature called [Remote Config](https://firebase.google.com/docs/remote-config). Using remote config we can change the behavior or even the appearance of our app without publishing an app update.

In the Colorandom toy app, I have introduced a feature where the app can show the color name using [thecolorapi.com](https://www.thecolorapi.com/).

![](https://cdn-images-1.medium.com/max/2000/1*bmf9f-F2NgMh17hFh-zyNw.gif)

Practicing what I preach, I figured it would be a good idea to protect this feature using a **remote config feature flag.** You can check the [commit](https://github.com/linakis/colorandom/commit/1e2599f12ae42efc72678e84fd213438fc8f2d2a) on how I did it. It all boils down to wrapping the entry point of your feature with an if statement.

    // protect the color-name feature with a remote feature flag
    if (RemoteConfigRepository.isFeatureColorNameEnabled()) {
        lifecycleScope.launch(Dispatchers.Main) {
            val colorName = fetchColorName(randomColor)
            name.text = colorName
        }
    } else {
        name.text = ""
    }

That’s all there is to it. If something goes wrong (spoiler alert: I didn’t handle the error responses of the API and the app crashed) then all you have to do is toggle the remote feature flag and in **real-time** your users will stop crashing.

![](https://cdn-images-1.medium.com/max/2000/1*u5MjCqDDZ4sx5x_m8aUukQ.png)

You now have all the time in the world to fix the issue and upload a new version to the store.

## Takeaways

* Don’t release and clock out. Plan your release so that you can monitor and support it during working hours.

* Never release to 100% of your user base. Always opt-in for staged rollout. This way at least the rollback is on the table.

* Make use of remote-controlled feature flags whenever possible to be able to toggle off a faulty implementation even when not in a staged rollout.