One, foreword

I was in charge of the server development of Vivo App Store, and was fortunate to witness the development of app store from one million days to tens of millions of days. After hundreds of iterations of the app Store client, the server has also been upgraded from single to service cluster and microservice.

In the process of rapid business development, the product is constantly iterating, and the server side is compatible with different versions of client API. On the one hand, let the team have a more thorough understanding of some of the existing design ideas of children’s shoes, on the other hand, it is also hoped to arouse the resonance of some peers who meet similar scenes and provide solutions.

Second, universal solutions

APP Store client iterations are frequent, and when a new version of the APP is released, it will inevitably result in multiple versions, which will result in multiple client requests on the server. Forcing users to upgrade the APP may lead to user loss, so it is necessary to adopt the coexistence of multiple versions. The following are references to some SOA service API versioning approaches discussed in the industry [1]. In principle, the following three schemes are inseparable from the actual development.

Scheme 1: The Knot No version — That is, there is only one version of The PLATFORM API. All users must use The latest API. Any MODIFICATION of The API will affect all users of The platform. (Figure 1 below)

Solution 2: Point-to-point: The platform API version has its own version number. Users can use the CORRESPONDING API based on their own requirements and upgrade the API by themselves. (Figure 2 below)

Solution 3: Compatible Versioning — Compatible Versioning. Like The Knot, there is only one version of The platform, but The latest version needs to be Compatible with The API behavior of previous versions. (Figure 3 below)

(From: The Costs of Versioning an API)

Simple analysis, The Knot only maintains The latest version, which simplifies The maintenance for The server, but requires service users to adapt to The latest version in time, which is not suitable for user products, but is more suitable for internal services. Point-point provides independent services for different versions of customers. With the increase of versions, the cost of development and operation and maintenance will increase. This will be used in the future when we are faced with “protocol upgrade”.

Scenario three should be the most common case, with server backward compatibility. The following cases mainly adopt this idea, and there are many ways to do this. Different strategies will be used in combination with specific business scenarios, which will be the focus of the following discussion.

3. Challenges and exploration of specific business scenarios

3.1 The Knot No Version and Point-to-point Mode

The picture above is a snapshot of our iterative changes in the App Store, with the following challenges as the business grows:

1) In the early stage of business development, as a service provider, the server should not only support multiple versions of app Store clients, but also serve the PC assistant on the software side;

2) The product form is varied, and the server interface change and maintenance are confronted with the challenge of compatibility with multi-version clients;

3) In terms of architecture logic, the server uses the earlier traditional architecture, which costs a lot to develop and maintain; Optimize and upgrade the protocol between the server and the client; And service unbundling is inevitable.

Therefore, server protocol, framework upgrade and public service separation are the primary direction of solution. The transformation went through two processes:

  • Phase A new version of the new interface adopts the new JSON protocol; The existing functional interfaces are compatible with each other. They are differentiated according to the client version and return different formats of different protocols.

  • Phase 2 With business iteration, all interfaces on the new version store are upgraded. In order to improve service stability, the performance of the old protocol cannot be significantly improved. On the one hand, the back-end architecture and framework are upgraded to improve development efficiency and maintainability. At the same time split and separate the new project, the implementation of the historical project is only available for the historical version. We separate independent services for heavy traffic, high concurrency, and basic service scenarios such as home page, details, downloads. At the same time, it also extracts some common internal RPC services, such as obtaining application details and filtering services.

After the transformation, the server architecture is shown in the figure above.

1) Old-service only needs to carry out corresponding maintenance work, corresponding to the point-to-point version.

2) As The internal RPC service only provides internal services, The server and client can be upgraded synchronously at any time as long as The latest version is maintained, adopting The Knot mode. It is important to note that the upgrade of the service needs to maintain backward compatibility. Be careful when extending or modifying fields, otherwise the client invocation may be abnormal when the service is upgraded.

3.2 Compatible Versioning: Compatible version control

Compatibility versioning should be the most common versioning method, especially in C/S architecture. Specific compatibility versioning policies are summarized as API version, client version number, function parameter markers, etc.

Scenario 1: API version control

With the development of the Internet, the user experience requirements are becoming higher and higher, and the form of products will also have different changes every year. In addition to avoiding aesthetic fatigue, it is also constantly exploring how to improve screen effectiveness, click-through rate and conversion. Take the first page of the app store for example.

The list of applications has undergone a single application double-row -> single-row -> single-row + interspersed layout in form. The content has also experienced the evolution of different commercial models, artificial scheduling and algorithms.

The internal logic of interfaces varies greatly with each version. Simply judging by version at the service layer can lead to extremely complex processing logic, and can have an impact on earlier versions. At the same time, the home page of the store is a very important business scenario. Considering risks, it would be more reasonable, simple and effective to add the version field in the interface URL for scenarios like this, and use different values for different versions. Different processing logic in the control layer would be based on different versions. Specific policies include adding the interface version field /{version}/index to the URL and carrying version parameters in the request header.

Scenario 2: Client version control

Like the home page list, the store’s interspersed Banner has gone through multiple iterations. As shown in the figure below. These interspersed styles appear in different versions, and different versions have different levels of support in terms of style layout and jump ability. When the interface is returned, it needs to carry out corresponding processing, adaptation and filtering.

Such scenarios can also be solved by upgrading the new interface as described in Scenario 1, but there will be a lot of duplicate code. In addition, the new interface will affect the transformation of client interface, especially some interface paths will affect the big data burying point statistics, and there will be relatively high communication and maintenance costs.

To improve code reuse. Using client version number control is the preferred strategy to consider. However, it should be noted that if you simply judge the client version number at the code level, the following problems need to be considered:

1) There will be various judgments at the code level, resulting in poor readability of the code, whether there is a more elegant method;

2) There is an objective situation. The client version number is indeterminate. As the client uses the train release mode reference [2], multiple versions are developed in parallel, resulting in the change of version number and discontinuous version jump from time to time, which also brings a lot of troubles to the server development.

How do you think about solving these problems? In fact, for some resources involved in different product forms or product modules themselves appear in different iteration cycles, they can be considered to have version or time attributes. From a programmer’s perspective, consider the client version of a resource supported as a member property of the resource object. Each resource with this attribute also has logical behavior corresponding to the member methods – filtering by attribute. Such a design assigns attributes and behaviors to resources, so that resources have uniform and flexible filtering capabilities, rather than simply hard-coding if-else judgment based on version.

Having a plan, it is easier to carry it out. Development allocates resource ids and sets corresponding supported client version ranges. Filtering logic is unified to resource objects.

At the code level, the filtering logic can be uniformly encapsulated into a utility class (sample code) that is returned to each business interface for filtering. A more elegant solution is to establish a unified upper class of resources, encapsulating the resource filtering method. All resource objects of resource bits implement this upper class, and unify the filtering ability in the acquisition of resource logic.

Scenario 3: Add function identification parameters

The app Store provides users with the ability to discover and download new apps and update existing apps. The store has incremental updates to reduce the size of the update package, so it is also called traffic saving updates to improve the user experience. In the early stage, we used the open source incremental algorithm, but it was found that the algorithm would take a long time to assemble and disassemble some machines, and even cause crash.

So the team sought a more efficient resolution algorithm. Similar to these existing interface enhancement scenarios, is there a better way to provide a new API or simply extend it internally by client version judgment? Because in addition to the known disadvantages of these schemes, we need to consider in the long run, such as the algorithm mentioned above, whether there is the possibility of upgrading in the future, and whether the download interface will have more capabilities.

Combined with the above thinking, in the original interface on the basis of the new flag parameter field, indicating that the request issued by the client support capability. For further extension, the field type is an integer value, not just a simple Boolean, and the server does the judgment logic through bitwise operations. Clients also get rid of the strong consistency between a feature and a version, without having to record that a version has a certain capability.

More thinking about interface design

Finally, some potholes and reflections. When providing an interface, the server should not only pay attention to the implementation of the interface, but also pay more attention to the user of the interface, the scenario they use, the call time and so on. Otherwise, the time spent in troubleshooting and maintenance of interface problems will be several times more than the actual development time.

1) Scenarioization: Specific to what is scenarioization, take the store client to help users check whether the application version installed on the mobile phone is the latest service example, detection timing is different scenarios, such as user startup, user switch wlan environment, timing detection, etc.. When you need to do a fine-grained analysis of which requests are valid and which lead to centralized requests, you can’t do it without a scenario distinction on the request. Therefore, when communicating the interface design with the client, please bring the scenario into consideration. For interface design, please refer to /app/{scene}/upgrade to define the name of each scene and bring the specific scene on the path, which will be of great benefit to the online request magnitude from different sources and problem analysis.

2) Authentication and service isolation: In addition to scenarios, interface calls should be recorded and authenticated as well as service isolation during allocation. For example, part of the interface service of the store is not only provided to the client, but also to the mobile phone system application call. At present vivo has hundreds of millions of stock users, so we need to be very careful. The amount of system application is not properly controlled, and the concurrency is much larger than the store itself. First, assess and communicate with the service caller in advance, and make a design to avoid problems. Even when problems occur, mechanisms should be in place to quickly identify problems, analyze the source of problems, and reduce the cost of problems.

So far the above ideas to solve the problem, and specific business and background have a certain relationship. As technology continues to evolve and evolve, there are more efficient solutions for mobile APP page dynamics, such as Google’s Flutter and Weex. These technologies offer flexible scalability, multi-terminal unity, and near-native performance. It not only reduces the client release frequency, but also reduces the server compatibility processing cost. At present, vivo also has a team in use and practice.

Technology changes, there is no best solution, only the most suitable solution. The development process not only meets the current implementation, but also considers the future scalability and maintainability. Development should not blindly pursue high-end technology, technology ultimately serve the business, adhere to the long-term, efficiency first.

5. Reference materials

1. The Costs of Versioning an API

2. Agile development and train release model