60% reduction in React Native application size in a few simple steps

I work at Mutual . She works in Brazil, in the field of equal credit. We help borrowers and lenders to connect with each other. The former seek good bets, while the latter seek income that exceeds what the market can offer them. Our product is used by a wide range of users, we work in a large country. As a result, our React Native-based iOS and Android apps download to very different devices.



It should be noted that the bulk of our users install applications on budget devices. We can draw such conclusions using the data from the Facebook device-year-class library.. This library, having received information about the model of the device, reports on in which year this device would be considered an upscale flagship phone. For example, the most popular phone among our users is the Samsung Galaxy A10. This phone, although it was released in 2019, could be considered the flagship only in 2013. Analyzing data on user devices, we can say that 85% of these devices could be recognized as high-end devices only in 2015 or earlier. Because of this, we have special requirements for optimizing our mobile application. The goal of optimization is so that even users with weak devices can comfortably use our application.


Percentage of devices that could be recognized as flagship in a particular year

In this regard, we paid close attention to the size of the application. In the case of its Android version, it was 26.8 MB. Although this is not such a big size, it is definitely more than the median size of our users ’applications. This figure, according to the Google Play Console, is 16.3 MB. The size of the application can be a decisive factor for users with limited tariff plans, or for those with low memory devices, which forces users to carefully select the applications that they will have installed. As a result, some applications have to be uninstalled. This is especially important in the case of the Mutual application, as borrowers pay monthly installments through this application. When the borrower uninstalls our application, the chances that he will make the payment on time are greatly reduced.And this directly affects the earnings of investors using our platform.


The size of the Mutual application is much larger than the median size of our users'

applications. The size of the application does not only affect the level of uninstallations. Size also affects the conversion rate of application settings. Here's a good article on this written by the Google Play team. This article discusses the importance of application size. In particular, it says that every additional 6 MB of the size of the APK file reduces the conversion rate of installations by 1%.

They also talk about the fact that in developing countries where budget devices are the norm, this effect is even stronger. Namely, reducing the size of the APK file by 10 MB in emerging markets corresponds to an increase in the conversion rate of installations by about 2.5%.


Increasing the conversion rate of installations for every 10 Mb of reducing the size of the APK file in different countries (according to Google internal data)

We were very motivated by how reducing the size of the application can affect the level of uninstallations and the conversion rate of application settings. As a result, we set to work aimed at reducing the size of the application as much as possible, taking into account its convenience for users. The first step in this project was to analyze the officialGoogle recommendations for Android developers.

Android App Bundle


Reading the recommendations, we learned that the easiest way to reduce the size of an application is to use a new application distribution method called the Android App Bundle (AAB). Until this moment, we distributed the application, collecting the good old Android Package (APK) file , which can be run on most Android devices, and uploading it to the Google Play Console. But the AAB bundle contains only compiled code and resources. As a result, when it is downloaded, Google is responsible for generating optimized APK files for various types of devices, taking into account their specifications and CPU architecture.

It turns out that by making a simple change to the build process of the project, can we get a serious reduction in the size of the application without making any more effort? This is too good for the truth!

After we read the documentation, we just changed the React Native Gradle build script so that it assembleReleasewould run instead of the current one bundleRelease. That's all we needed to create the AAB file. After some more modifications made to the Fastlanesupply configuration regarding automatic uploading of materials directly to the Play Store, we switched to AAB, and a new version of our application appeared in the Google Play Console.

This change alone has led to a reduction in the size of APK files transferred to user devices. The decrease ranged from 9.1 to 12.4 Mb. As it turned out, the use of the Android App Bundle is an effective technique that, indeed, allows you to reduce the size of the application.


The old APK-file and the new AAB-bundle, the use of which leads to the fact that the size of the application on different devices can be from 14.4 to 17.7 MB

True, you should be careful here. If you use React Native with Hermes , then you may need to update your dependencysoloader(see details here ). Otherwise, there is a risk of giving the user an application that will contain a critical error. We were lucky, we were able to identify this problem during testing of the alpha release of the project. But the error could easily slip into production, since it does not appear during local testing or when building the APK file.

Optimizing application resources using Android Size Analyzer


The next suggestion for optimizing applications that we found in the documentation was the use of Android Size Analyzer . This is a command line tool that analyzes Android applications and looks for ways to reduce their size. We launched this tool using a command of the following form:

size-analyzer check-bundle [BUNDLE].aab

As a result, we got a list of great application resources and images that we can optimize. We were also recommended to set up ProGuard.


Size-analyzer report

▍ProGuard


ProGuard is a tool for compressing, obfuscating, and optimizing Java bytecode. We have not yet explored the possibility of using this tool, as we learned that it may not be compatible with some Android libraries. Since we strove to reduce the size of our application as quickly as possible, and to make this as simple as possible, we decided to leave this optimization method in the future.

▍Large app resources


Running it size-analyzeragain, with the key -d, we got a list of application resources, sorted by their size. Since this tool does not know anything about exactly how users work with the application, it gave us the opportunity to independently decide which resources should be removed and which should be loaded into the application dynamically.


List of large application resources sorted by size

The first and largest resource on this list was the React Native JavaScript bundle. Now we cannot split this bundle and load it dynamically. But later we will think about it. Further down the list are large font files (TTF) and image files (JPG and PNG).

▍Unnecessary images


Our attention was immediately drawn to the huge JPG images used in the Storybook . We use this system to develop and test components. This is 2 MB of garbage that fell into the production version of the project. Shameful mistake! When something like this happens, we feel like we have done some serious stupidity. But in the complex world of software development, everyone makes mistakes. I believe that if you talk about your mistakes publicly, it will help other developers learn from these mistakes. There is a possibility that you make the same mistakes if you do not analyze the resource structure of the application, the size of which is gradually increasing.

▍ Fonts


After we quickly got rid of unnecessary images, we proceeded to analyze other elements of the resource list. It was clear that there were a lot of built-in fonts in it. After we talked about this with our designers, they told us that many old components do not differ in strict adherence to typographic manuals. Therefore, we found out which components can be removed, and in which you can use the appropriate updated fonts. Thanks to this, we were able to reduce the number of fonts used from six to four.

Another thing we noticed was the sheer size of the font files themselves. The size of each of them was approximately 670 Kb. This meant that four fonts in the uncompressed bundle occupy a stunning 2.7 Mb. But, fortunately, there is a tool called FontForge that allows you to deeply analyze and modify font files. Using this tool, we were able to find out that the main reason for the large size of font files are extended Cyrillic characters and unnecessary glyphs. We could safely remove all this, since the text part of our application is completely written in Portuguese. Thanks to this change, we were able to reduce the size of the font files from 670 Kb to 70 Kb - by 90%.


Examples of some glyphs included in our fonts.

Due to the fact that we got rid of unnecessary fonts and optimized the remaining ones, we were able to reduce the application size by 3.8 Mb. This led to a pleasant reduction in the total size of the APK file by 2 MB.


List of fonts and their sizes before optimization


List of fonts and their sizes after optimization

▍Optimization of images


After analyzing the images that still remained in the application, we noticed that some of them are quite large. We processed several of these images with the TinyPNG graphics optimization tool and saw a significant reduction in the size of these images. After that, we decided to optimize all the JPG and PNG images used in the application. There were 41 of them.


Image before and after optimization

This gave us the opportunity to reduce the size of images from 2.5 MB to 756 KB, that is, by 70%. However, due to the fact that the images were not previously optimized, they were compressed during the creation of the final APK-file. It turned out that our optimization led to a decrease in the size of the application downloaded by the user by only 500 Kb.

After that, we realized that we had already exhausted all the possibilities of quick optimization. Continuing the optimization of resources would require either great efforts or only minor improvements.

React Native JavaScript Bundle Optimization


After we figured out the application resources specific to the Android platform, it's time to analyze the JavaScript bundle. Bundle optimization can be considered a good idea for three reasons. Firstly, it reduces the size of the finished APK file. Secondly, this leads to a faster launch of the application, since the JS virtual machine has to process less code. Finally, and most importantly, it speeds up the OTA updates we do several times a week using CodePush .

▍ Bundle analyzer and code optimization


In order to make a decision on how to reduce the size of the bundle, first, you had to figure out what takes the most place in the bundle. To do this, we used the excellent open source tool react-native-bundle-visualizer . After analyzing the project with its help, we got a visualization in which every folder and every application dependency was present, indicating the sizes of the corresponding entities.


Snapshot of the libraries and folders of the code base of the frontend of the Mutual application with the indication of sizes

We found out that the total size of the bundle is 5.49 Mb. 57.8% of this volume is represented by dependencies from the foldernode_modules, 27.5% - application code. What remains, the tool used by us could not be identified. During the assembly process of the bundle, unused code was already deleted, so what we saw was code that is actually used by the application. But, even considering this, in the bundle you can always find something that can be improved.

The biggest project dependency ismath.js. This library, as its name suggests, implements many mathematical operations. We do not have a special need for this library due to the fact that we perform all the important calculations on the server, after which we simply send the results to the application. When we looked at the application code, it turned out that the library is used only to perform some simple operations. It was most likely used by a developer who worked on server code only by virtue of habit. We quickly extracted the appropriate methods from the library and built them into our code base, completely getting rid of this dependency. This allowed to reduce the size of the bundle to 4.64 Mb. Refusal of only one library allowed to reduce the size of the bundle by 15.5%!

As already mentioned, we use the Storybook to develop and test components. True, the corresponding capabilities should be available only in local and intermediate environments. End users do not need this. Thanks to this, we used the variable ENVIRONMENTto control the inclusion of the corresponding part of the application. Although this is suitable for restricting access, the bundler cannot know what value is written to this variable. Because of this limitation, all Storybook code fell into the production bundle.

In order to fix the problem, we isolated the import of this section within a single file. Then we created two versions of this file: one that includes the Storybook, and another that is for production, which contains only the component layout. To switch between these files when preparing the production version of the bundle, we wrote a script that is executed before the project is built and changes one file to another. Thanks to this method, we were able to completely remove the Storybook code from the production version of the application, removing both the dependency from node_modulesand the usual code regarding the configuration of Storybook “stories”.


Updated Storybook folder with two versions of the index file.

These two changes reduced the bundle size from 5.49 MB to 4.2 MB. This means that, among other things, the application will load faster and update faster.


The size of the final bundle was 4.2 MB.

After all these improvements, we again downloaded the application in the Play Store. Now we were informed that the size of the finished APK-file will be in the range from 10.5 to 13.7 Mb. This, given the fact that the application originally had a size of 26.8 MB, simply means an incredible reduction in project size by about 60%! So, as we said in an article by the Google Play team, we can fully expect that the conversion rate of installations will increase by 3.75%.


Comparison of the original APK version of the application with the final AAB version, in which all the improvements described above are made

Summary


We, as business-oriented programmers, know that sometimes companies, for the sake of a faster development of a project, may well go on to increase its technical debt. This is especially true for young startups like Mutual, who are trying to find their place in the market. But if you do not observe this debt, you can make annoying mistakes, such as sending 2 MB of test images to production and unjustified use of a huge library. Technical debt should not be allowed to get out of control and cause trouble.

In addition, it often happens that developers simply miss the opportunities available to them to optimize projects. Therefore, it is sometimes recommended to critically analyze your projects. Just to check if you missed some opportunity to quickly optimize the size, speed, or some other aspect of the application. It took us only two days to analyze the application, plan the work and make improvements to the project, which allowed us to reduce the size of the application by 60%. It is difficult to come up with something else that can produce the same results in such a short time.

How do you optimize your React Native projects?


All Articles