The What, Why, and How of Mastering App Size
Our daily tasks as engineers often involve implementing new functionalities. Existing users get to enjoy the benefits of these features, new users are enticed to sign up for the app, and we get to write more code. At first glance, it appears to be a perfect symbiotic relationship — a win-win-win scenario where everybody’s happy and nothing goes wrong, right?
But sometimes a shiny new feature brings more harm than good. The reason is simple — application size. Any addition to the application — be it code for a new feature, an image resource for a new button or even support for a new localization — contributes to the increase of the application’s size. You could ask, “Is this really a problem in our modern world?” We would argue that it is.
So, what exactly is app size?
While the answer may seem simple, it actually isn’t that straightforward. We can talk about app sizes in at least four ways:
- Download size:
The size of the app when downloading the app from the user’s app store, i.e., the amount of data transferred over the internet. To transfer as little as possible over the internet the app download size is compressed.
- Install size:
The size of the app directly after installation on a mobile device, i.e., the amount of data stored on disk after an app has been installed. To be able to use the application, the app install size is the content of the application in uncompressed form.
- Storage size:
The size of the app when it is in use, i.e., the install size plus the space taken up by various caches (audio files, images, etc.) stored on the device.
- Update size:
The size of the app when downloading an update from the App Store or Play Store. This can be smaller than download size, depending on how much of the app has changed.
Each of these app sizes affects users differently, but we can generally categorize their impacts under two domains: network related and storage related.
Why is app size so important?
In the digital age, more and more communities can embrace fast, affordable mobile connections, with ubiquitous Wi-Fi access in cities and unlimited fiber connections in homes, along with up to 1TB of storage space on smartphones and tablets. While this is true for some, our data reveals that a substantial number of individuals in emerging markets use older-generation smartphones with less storage space — e.g., iPhone 6s with 16GB of storage space, a portion of which is already claimed by iOS itself. Consequently, these users are forced to choose which content is most valuable to them and can be kept on their devices. Given that emerging markets account for a staggering 85% of the world’s population, app size becomes exponentially more important for our users across the globe. While these challenges may seem theoretical, we possess data that prove how real these issues are.
Google conducted an experiment providing users with identical builds of apps, artificially inflated in size. The experiment revealed that for every 6MB increase to an app’s size, the app’s installation-conversion rate decreased by 1%. The implications are profound, leading to a multitude of missed opportunities.
However, there exists an aspect of app size that is less apparent but equally consequential.
We often hear about global warming and the effects cars, cows, and power plants have on it, but the effects of our apps — not just using apps but downloading updates, as well — aren’t usually associated with this issue. Every week, millions of users download new versions of their apps. For example, we found that Spotify apps (iOS and Android) in 2022 were updated more than 20 billion times. Multiply this number by the download size of the apps, and you’ll get a staggering 930+ petabytes of network traffic. We can map this traffic to the power consumption for transmission. Utilizing the one-byte model developed by the Shift Project, we found that transferring one byte through the network consumes 1.52e-10 kilowatt-hours (kWh) for Wi-Fi and 8.84e-10 kWh for mobile. So even if we assume an optimistic scenario of 100% Wi-Fi usage, this traffic translates to a minimum power consumption of approximately 150,000,000 kWh. There is no precise way to convert this to CO2 emission, but we can take EPA data for 2019 and deduct that this equates to around 65,000 tonnes of CO2.
To put this figure into perspective — 65,000 tons of CO2 is roughly equivalent to the emission generated by 65,000 individuals taking a round trip flight between London and New York. So even if we decrease the app size of both our apps by just 1MB, we’d reduce our yearly carbon footprint by 560 tons of CO2 annually. By making our apps smaller we’re not only helping our users save some space on their devices, but also helping make our planet a bit cooler.
App-size safety nets
To guard against uncontrolled increase in app size, we’ve incorporated safety nets into various points of our development process.
Every time someone initiates a pull request (PR) in our client repositories, an automatic check swings into action to evaluate how this PR impacts app size. These checks are a mandatory part of CI, and the code can’t be merged until these checks are completed.
To perform this check, we rely on two critical builds: the base build, which reflects the current state of the master branch, and the head build, which mirrors the master branch but includes the changes from the PR, rebased on top of it. Once we have these two builds ready, we submit them for processing through Emerge Tools.
Emerge processes these builds and analyzes the differences in app size between them. Subsequently, we receive a comprehensive report with the results of the check.
Once the results are available and reported back from Emerge, the report is conveniently accessible directly within the pull request.
When looking at this data, how do we know if the benefits of the changes we want to merge cover the costs of corresponding app-size increase or not? For that purpose, we’ve established a threshold of 50KB. Everything below that threshold can be safely merged without any additional action from the developer. The code changes that exceed the threshold are blocked and an approval is required from a dedicated team that monitors and observes these cases. This team takes multiple factors into consideration when determining whether a pull request that surpasses this threshold can be merged.
To enhance observability and allow us to keep a close eye on the impact the changes have made, we’ve set up a dedicated Slack channel where we can receive information about every pull request that gets merged and exceeds the threshold. Having all these significant contributions to our app size neatly organized in one place makes tracking and observing these changes significantly easier.
This channel isn’t solely for identifying problems — it’s also a place for celebrating successes and sharing positive news. The same 50KB threshold applies to PRs that successfully reduce the app’s size. If a code change positively impacts app size and exceeds the threshold ( less than 50KB), a notification is displayed within the same channel.
By implementing these app-size checks and notifications, we effectively manage our app’s size while ensuring that both the development team and the stakeholders maintain a clear view of the impact they’ve made through these changes.
Attribution to owner teams
Having safety nets is a great way to prevent massive changes from sneaking into our codebase. But most pull requests tend to be smaller and don’t set off the threshold alarm, so there are still challenges when it comes to this slow “under the radar” growth. Over time, these seemingly harmless additions to the codebase start to accumulate. Given the volume of pull requests we merge daily, it’s only natural that our app’s size would gradually expand. It’s like a snowball effect — each PR, no matter how small, contributes its tiny piece to the puzzle. So while the alarms might stay silent, the app size continues to grow.
To conquer this problem, we are evaluating the download and install size of each compilation unit and resource file in our codebase, while also attributing it to the dedicated team that owns that module.
In contrast to PR checks, we only need one build for this job — specifically, the most recent snapshot of the master branch at the time of execution. We upload this build to Emerge for analysis, and in return, thanks to Compilation Unit Attribution, we receive a detailed breakdown of compilation units and how much each one contributes to both the download and install sizes. We then enrich this data with resource sizes collected using an in-house set of scripts.
After gathering all this data, we process it and group the files by modules. And since each module has its own oversight team, we can pinpoint who is responsible for what.
The beauty of it all is that the data aren’t just raw numbers. It’s presented in a user-friendly interface that allows each team to visualize and keep tabs on their app-size contributions over time.
To make things even more insightful, we also provide statistics that compare each team’s app-size contribution to other teams within Spotify.
Establishing attribution and holding each team responsible for its impact allows us to effectively maintain a balance between rolling out new features and ensuring that our app size remains within reasonable limits.
Working with app size in practice at Spotify
Why it’s harder than it sounds
It’s easy to say that developers should prioritize optimizing their code for size, but it’s not always that simple.
One reason for this is that optimizing for size is not a free action. Whenever you refactor a piece of code so that it results in a smaller binary, you are very likely moving that problem to a different area, which is most often either performance — the code is now slower as a result — or architecture — the code is now harder to read/manage. In other words, optimizing for size is a trade-off, and the conclusion is drawn that this trade-off is not worth it.
Another reason is that sometimes reducing the size impact of a particular piece of code simply isn’t feasible. We find that most pull requests that trigger our alerting systems are relatively easy to fix, but there are many cases where the source of the unnecessary overhead lies not in the code itself, but rather a particularity of how the programming language — or even the operating system itself — works. While these are not impossible to treat, they often require changing how the app is structured in ways so extreme that we cannot justify doing so just to avoid one regression.
The third and final reason is that sometimes you simply cannot run away from it. Any added code will result in a larger binary, so if you’re working on a big and complicated feature, it may simply be the case that increasing the app’s install size can’t be avoided. To account for cases like these, we have created something that we call the App Size Policy.
App size policy & exception process
The policy is, in essence, a big document that explains why we care about app size and the systems around it, and it includes general platform-specific tips on how to optimize for code size. But most importantly, it defines an exception process that can be followed when developers believe their pull requests should be allowed to considerably regress the app’s install size.
In most cases, the process to be granted an exception requires proof that the proposed change will have a positive impact on the business.
To ensure that our tools bring value, we developed a set of metrics and dashboards that we continuously monitor.
However, the effect of the checks extends beyond PRs that raise warnings or dashboards that show valuable app-size data. We receive app-size-related questions in the dedicated Slack channel regularly, and we have found that Spotify engineers love to make their PRs smaller, resulting in a roughly calculated total PR size decrease of 20MB.
App-size growth is hard to avoid when adding new features or making improvements. But with the proper guidance, monitoring, and alerts, we can make educated decisions about what is and what isn’t worth adding — and when a new functionality can result in a win-win-win situation.
Tags: engineering leadership, Mobile