Search⌘ K
AI Features

Code Optimization in Mobile App

Explore key code optimization methods to reduce app size and enhance mobile performance. Understand automated shrinking tools, on-demand delivery, and build splitting to create leaner apps with faster installs and smoother user experiences.

In today’s mobile-first world, users demand apps that launch instantly, scroll smoothly, render beautifully, and respond without delay. Any lag, stutter, or slowdown can lead to user frustration, negative reviews, or app abandonment.

Performance optimization is about refining every layer of the mobile experience: from how efficiently code executes to how data travels over the network. In this chapter, we’ll take a systematic approach to performance, starting with the foundation of any mobile app: the code. By writing clean, efficient, and well-structured code, we lay the groundwork for a performant user experience. Let’s understand this with a real scenario we all might have encountered.

Have you ever scrolled through an app store, found what you were looking for, but hesitated when you saw the download size? The most successful apps, from Instagram to X, are remarkably lean. This is no coincidence. A large app size is often the first reason a potential user gives up, deleting the app before they’ve even experienced it.

We don’t want users to abandon our app because it seems too big to download or looks like it will clog up their phone. And the challenge doesn’t stop at the download. Once installed, a heavy app can slow down the device, drain the battery, and quickly find itself in the “uninstall” pile. This is why optimizing our code to reduce app size is one of the most critical steps in development; that’s what we’ll be discussing in this lesson.

So, how do developers achieve these significant improvements? Let’s explore the most effective techniques to optimize code to reduce app size.

Strategies for reducing app size

A critical part of code-level optimization is minimizing the size of the app binary. A smaller app not only reduces download and install times but also consumes less storage, improves cold start performance, and makes updates more seamless, all of which contribute to a smoother user experience.

Let’s explore key strategies to systematically reduce the app size without compromising functionality or user experience:

Automated code and resource shrinking

The most powerful way to reduce an app’s size is to use the automated tools built into the Android and iOS development environments. These tools analyze your app, discard unused parts, and optimize the remaining assets. The diagram below illustrates the significant impact these tools can have on an app’s final size:

An example of app size reduction using platform tools like R8 and app thinning
An example of app size reduction using platform tools like R8 and app thinning

On Android, the R8 compiler automatically handles app optimization. It combines several powerful techniques, mentioned below.

  • Tree-shaking: This is the core process for removing unused code. R8 scans our app’s entry points (like activities and services) and builds a graph of all the code that is actually reachable. Any classes, methods, and fields outside of this graph are considered unused and are “shaken out” (completely removed from the final app package).

  • Resource shrinking: After the unused code is removed, R8 can then identify and remove any resources (like images, layouts, or strings) that are no longer referenced.

  • Obfuscation: As a final step, R8 renames our remaining classes, methods, and fields to shorter names (e.g., a.b.c), which further reduces the final .dex file size.

Best practice: While R8 is enabled by default, we must configure the proguard-rules.pro file to specify any code that should not be removed or renamed (e.g., code accessed via reflection). This prevents runtime crashes.

On iOS, optimization (referred to as app thinning) is handled by a combination of compiler features and the app store’s delivery system :

  • Dead code elimination: The compiler automatically performs “dead code elimination” during the build process. Similar to tree-shaking, it analyzes our code and removes any functions, methods, or variables that are never actually used. This happens by default in release builds.

  • App slicing: This is the most important part of app thinning. It ensures that when users download an app, they only get the assets needed for their specific device. For example, a user with an iPhone SE won’t download the high-resolution 3x images designed for an iPhone Pro Max. This is managed by organizing resources in asset catalogs within Xcode.

Best practice: Ensure our project is configured to use asset catalogs for all images and resources. This allows app slicing to work effectively. Using the app size report in Xcode, we can check the final download sizes for different devices.

Impact: While the exact numbers vary by project, developers typically see significant results from these automated tools:

  • Android: It’s common to see a reduction in the final app package size by 20-50%, especially in apps with large codebases.

  • iOS: Download size is often reduced by 20-40% through the combined effect of dead code elimination and asset slicing.

Automated shrinking optimizes the code and assets already in our app. The next strategy, on-demand delivery, takes optimization a step further by controlling which parts of our app are downloaded first.

On-demand delivery

Another effective approach to reducing app size is to avoid bundling all features into the base installation. Instead, we can defer the delivery of certain modules until they are actually needed, a strategy known as on-demand delivery. This technique is especially useful for apps with modular or optional features, such as advanced settings, large media packs, or game levels. For example, rather than shipping all game levels at once, a gaming app can include only the first few in the base APKAndroid Package Kit or IPAiOS App Store Package, and allow users to download subsequent levels dynamically as they progress. This not only keeps the initial download lightweight, but also aligns resource usage with actual user behavior.

This approach fundamentally changes how users receive features, as illustrated below.

On-demand feature delivery as we progress (log in) within the app
On-demand feature delivery as we progress (log in) within the app

The on-demand delivery technique works as follows:

  • Dynamic features: Developers split the app into self-contained modules (e.g., “chat,” “video player,” or “AR scanner”) and flag each for on-demand installation. This keeps the initial app package small and lightweight. For example, a shopping app might only include the “browse” and “checkout” features in the initial download. The “order history” feature is then downloaded from the app store automatically the first time a user taps “My Orders.”

  • User behavior-based delivery: Features and assets are delivered contextually based on user progress or actions. For example, a language-learning app wouldn’t include every language pack in the initial download. Instead, it downloads the specific lesson pack for Spanish only after the user selects Spanish as their language of choice.

For very large feature sets, the app can be designed to respect the user’s data plan by prompting them to download features only at convenient times. For example, a social media app introducing a new, multi-megabyte video editing suite might ask the user, “Download our new video editor (100 MB)? We recommend using Wi-Fi before starting the download.”

On-demand delivery is implemented using platform-specific features such as Google Play’s Dynamic Deliveryhttps://developer.android.com/guide/playcore/feature-delivery system for Android and Apple’s On-Demand Resourceshttps://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/ framework for iOS. These mechanisms enable developers to build modular apps that optimize both the initial download and the delivery of features or assets at runtime, resulting in better performance and a more tailored user experience.

Impact: By implementing on-demand delivery, developers often report a reduction in the initial download size by 20-60%, which dramatically improves the first-time user experience and increases the likelihood that the app will be downloaded and kept.

Test your understanding

1.

A language learning app lets users download new lesson packs only when they finish the current level. What is the main benefit of this approach?

A.

It saves storage space on the user’s device and speeds up initial installation.

B.

It makes all features available immediately, even before the user needs them.

C.

It increases the overall app size.

D.

It prevents the app from working offline.


1 / 1

On-demand delivery optimizes what is delivered to the user based on the features they need. The next strategy, build splitting, further optimizes the download by tailoring it to what the user’s specific device hardware can run.

Build splitting by architecture

Modern mobile devices use different types of processors (CPUs), each with its own architecture, also known as an Application Binary Interface (ABI). To get the best performance, developers often include code optimized for each specific architecture. Build splitting is the process of ensuring that users only download the code that matches their specific device, rather than a universal package containing code for all architectures. The following diagram shows how a single app bundle is split to serve different devices.

Optimizing app downloads by targeting specific device architectures
Optimizing app downloads by targeting specific device architectures

An explanation of this process and its operational flow is presented below.

  • Developer uploads a universal bundle: The developer compiles their app and uploads a single, universal package to the app store. This package (an Android App Bundle (.aab) for Google Play or an app archive for Apple’s App Store) contains the optimized code for all targeted architectures (e.g., ARM64 for modern phones, x86-64 for emulators or specific devices).

  • App store generates specific packages: The app store’s back-end system processes this universal bundle. It automatically generates multiple, lean versions of the app, each containing only the code and resources for one specific architecture.

  • A tailored app is delivered: When a user presses “Install,” the app store detects the architecture of their device and automatically delivers the corresponding tailored, smaller package. The end-user is unaware of this complexity; they simply benefit from a faster download and a smaller app on their device.

Impact: The impact of build splitting is a leaner installation for every user. By preventing the download of unnecessary code, developers typically see a download size reduction of 10-40%.

Test your knowledge

You are optimizing a ‘Travel Companion’ app. You’ve identified three main performance issues:

  1. The app’s initial download is huge because it includes offline maps for every continent, a feature most users never need.

  2. The code contains large, third-party libraries, but your app only uses a few functions from each. The rest is unused ‘dead code’.

  3. The app needs to support both modern 64-bit phones and older 32-bit devices, forcing everyone to download all the compiled code.

For each of these three issues, what primary optimization strategy would you use to solve it?

Say “Hi” to Ed (the AI persona) in the widget below to test your knowledge.

If you’re unsure how to do this, click the “Want to know the correct answer?” button.

AI Powered
Saved
15 Attempts Remaining
Reset

Conclusion

In this lesson, we focused on the foundational code optimization strategies that make an app as lean and efficient as possible before it is even opened by the user. By using automated shrinking, on-demand delivery, and build splitting, we can dramatically reduce an app’s size, which enables faster downloads and installations and saves valuable storage space.

However, a small app that installs quickly is only half the battle. Once a user taps the icon, a new performance challenge begins: ensuring the app loads instantly and feels responsive from the very first moment.

In the next lesson, we will shift our focus from download optimization to runtime optimization, exploring the critical techniques for optimizing the app’s loading and ensuring a swift, seamless launch experience.