Once a mobile app version is installed by a user, there is no guarantee that they will ever update it, so that version of the app could be ‘in the wild’ forever, i.e. every version over time continues to exist (which is what I mean be four dimensional). See Why don’t users update apps?. This means you have to be very careful with integrations to anything outside of the shipped app package. You can’t rely on the current state of your code base to tell you how all the live versions of the app are interacting with these interfaces.
This has the following consequences:
- You need to take care when changing service APIs used by the app. You need to make sure they are backwards compatible.
- Remote config becomes difficult to manage. Changing a remote config value changes it in all version of the app. Some examples:
- You can’t develop a feature behind a remote config flag, then turn it on when it is completed. It would be turned on for all versions, including those where the feature was incomplete.
- If a feature relies on the value of a remove config value to be ‘on’, you can’t remove that remote config item in the future, without turning the feature ‘off’ for users of older versions.
- You can’t use a simple boolean flag across multiple versions of the app. If you have to switch a feature off in one version because it didn’t work, you can’t switch it on in a future version when it’s fixed without switching it back on for the older versions.
- Just because something isn’t referenced in the code, it doesn’t mean that it isn’t live in the app - see https://adambennett.dev/2021/06/a-cautionary-tale/
- You need to add analytics, kill switches, force upgrade mechanisms etc before the version you want to analyse / kill / force upgrade. This requires either very good forethought, or time travel. It’s easy to think ‘let’s add analytics to see how the old version is used’. But users on the old version won’t update and get your analytics. (If they did update, you wouldn’t need the analytics).
How to mitigate:
- Use analytics to track how many people are using different versions of an app, and use specific analytics to track how specific integrations are being used.
- Create clear documentation that describes the versions when integrations were added or removed. For example in a legacy remote config value, you could include the app versions it was used by in the description, e.g. `WARNING used up to v1.0.32, do not remove until no-one is using this version.
- Use versioning on you APIs. This allows you to make changes, whilst still being backwards compatible.
- Add a force upgrade mechanism, that can force users to upgrade if they are below a minimum app version (use with care, this is terrible UX, because it adds friction to using your app, and not all users can update - Why don’t users update apps?)
- Limit remote config to ‘emergency use’ only - e.g. if you are releasing a risky feature, you might want a way to switch it on and off remotely. You could:
- Create a flag that defaults to the feature being on.
- Release the feature.
- If all goes well, remove the flag and the feature will continue to be switched on.
- If it goes badly, use the emergency switch to switch it off. From this point on, you’ll need to keep the original remote config flag until that version is no longer used. You’ll also need to add a different flag to switch it on in the new version.
- Use more complex logic in feature flags, e.g. by specifying which versions should be switched on / off. Note this needs to exist up front, and can’t be retrofitted to older versions.