Renuo's latest project, City Messenger, was launched in October 2019 on the App Store and Play Store. Since then, we've had plenty of time to gather customer feedback and pursue our own new ideas. This has resulted in a completely renewed version, which will be available soon.
In this new version, however, some changes were introduced which were no longer or only partially back-compatible with the first version of the mobile app. This inevitably led to the fact that we had to take a step back before new features could be added.
The first version defined the code foundation or "cornerstone of the app" relatively statically. All functionalities were developed to work well with the first version of the app.
When the second version of the app was developed, the question of how to deal with the old version quickly arose. Because unlike a website - which you can at least partially control yourself - apps allow users to control whether they update the app or not. To understand why this is a problem, it is important to know what the architecture of a modern app (typically) looks like.
To make City Messenger work, it needs - in simple terms - three components:
- The mobile app, or in technical jargon the frontend (actually a ReactNative frontend) is what customers download to their smartphone and see when they open it. The app is responsible for displaying chat histories, accepting user input and thus forms the top layer to the end user.
- The server or the so-called backend (more precisely Ruby on Rails backend) is responsible for receiving messages and forwarding them to the right recipient. It manages the user data and processes/validates all entries made by the users via the app. Thus the server forms the second, much larger layer.
- The database is responsible for storing all users, shops and chat histories. It creates the data in such a way that they can be read and processed by the server. Thus it is the basic framework of the whole and forms the third layer on which everything is based.
Schematically, the architecture looks like this:
Each layer is detached from the others. Nevertheless, they must communicate with each other with a predefined behavior.
As a comparison, we take Swiss Post as the manager of parcels ("backend"), which delivers mail to private individuals ("app"). This is a standardized process: the recipient has an address in a certain format that the postal service can read. If the address is correct, the recipient will one day find a parcel in the parcel box. However, this will only happen if all processes have run as planned by then. If there is a malfunction somewhere, the delivery will either be delayed or not at all. This can be simplified as follows:
If you now transfer the scheme to the City Messenger architecture, you can see a certain similarity:
The shear of the different versions
If we return to the statement in the first section that the users have control over the updates, we notice a problem in this architecture: If a new "negotiation process" is defined due to the new app version, with which the app and the server communicate, this assumes that both understand each other, that both use the same process. But if the app still uses the outdated process - because the user has not updated the app and we as developers have no control over it - the server must react differently depending on the strategy. Either it no longer accepts the app's communication attempt until it is updated, or it supports it for the time being and runs on two tracks, so to speak.
To apply this to the Post example: If, for example, the Post ("backend") changes all postal codes to a new combination of alphanumeric characters (8400 becomes A12D9) and a sender ("app") now tries to post a mail with the old postal code system, the Post also has two options to react Either it is accommodating and accepts the shipment despite the wrong address and delivers it correctly anyway. Or it rejects the item. Both strategies have advantages and disadvantages.
The "goodwill strategy" / backwards compatibility
The biggest advantage of this strategy - provided that all app versions are supported by the device - is probably that all customers are still served, even if they have decided not to update the app. So if you have a large user base, this strategy certainly makes sense. You hardly want to lose users just because a segment can no longer be served.
However, this strategy also has some disadvantages. The most important one is the maintenance of several versions, which must be compatible with each other. This involves a lot of extra work, because you have to design new versions carefully so that previous functions are not accidentally changed.
In addition, if customers do not update to the second version, they may not update to the third and fourth versions. Thus, bug fixes must always be implemented several times to take older versions into account.
The "Rejection Strategy" / Version Enforcement
The advantage of this strategy is that, as a rule, everything has the same structure. This means that the functionality only has to be developed once. For a new version, it can simply be changed without "slamming the door in the face" of certain customers. The development effort is correspondingly smaller.
However, there is also a snag with this strategy: When customers open the app, the first thing they have to do before they can use the app is to update it. This is interpreted as weak UX (user experience) and can lead to user loss.
However, if the users actually update the app, the new features lead to an improved UX, especially for chat applications. And the more people use a new chat functionality, the more attractive it becomes.
Knowing about your users helps!
Which strategy to choose must depend on the end customer. It is therefore worthwhile to install a mechanism at an early stage with which the update behaviour of the users can be monitored and controlled. The example of City Messenger showed that most customers regularly updated the app or activated automatic updates. Thus, 10 days after the release (February 23, 2020) most installations had already been updated. The activation rate also increased steadily, i.e. the number of customers who opened the app after the update or performed a new installation.
In the case of City Messenger, we have therefore opted for the rejection strategy. This resulted in advantages, which were predominant:
- Users profit more quickly from new developments, because everyone can use them.
- The development speed is faster, because we don't have to worry about backwards compatibility. So the improvements of the apps are always in focus.
- We prevent the creation of two different data versions, which could lead to the loss of customers.
To have the update tracking installed afterwards is usually complicated and requires a lot of time. (For City Messenger, it took an additional 35 man-hours.) Therefore, it is useful to think about how you want to proceed with your product before an app release. Especially if other components are to access the data in the future, it quickly becomes complicated.
Since every customer behaves differently, it is also advisable to study the update behavior with the first patch releases, so that no surprises occur with a major new release.