App Size Costs You Installs Before Anyone Opens It

Table of Contents
- The Users You Lose Before Launch
- Where the Megabytes Actually Are
- Reducing Download Size
- Startup Is Several Distinct Phases
- What Blocks the First Frame
- Deferring Work Without Breaking It
- Measuring on Real Devices
- Preventing Regression
- Common Pitfalls
- Conclusion
- Frequently Asked Questions
Key takeaway: Download size and cold start time are conversion metrics, not engineering vanity metrics. Both are usually dominated by a handful of dependencies and initialisation calls that can be removed or deferred without changing what the app does.
The Users You Lose Before Launch
Two failure points occur before a user has any opinion about your product.
The first is the download. A user on a mobile connection, or with limited storage, or on a metered plan, weighs the size against their interest. Install conversion declines as size grows, and the decline is steeper on slower connections and cheaper devices — which is to say, in the markets where growth is usually easiest.
The second is the first launch. A cold start that takes eight seconds on a mid-range device produces a meaningful share of users who close the app before it finishes. They never see the onboarding you designed.
Both are invisible in typical development. Engineers install from a build system rather than a store, on flagship hardware, over office networks. The experience that determines conversion is a user on a three-year-old mid-range phone over a congested mobile connection, and nothing in the normal workflow surfaces that.
The encouraging part is that both are usually addressable without removing features. Size is typically dominated by a few dependencies and unoptimised assets; startup is typically dominated by initialisation that did not need to happen before the first frame.
Where the Megabytes Actually Are
Before optimising, find the actual distribution. Both platforms provide tooling that reports size by component, and the results are consistently surprising.
The usual contributors, roughly ordered:
Images and media. Frequently the largest category, and frequently the easiest to reduce. Launch images, onboarding illustrations, and icon sets shipped at resolutions no device uses.
Third-party dependencies. A library added for one function that brings a substantial transitive tree. Analytics and advertising SDKs are notably large relative to what they do.
Fonts. A full font family with weights and styles nobody uses. Subsetting to the characters actually needed is dramatic for non-Latin scripts.
Native libraries for unused architectures. Shipping binaries for architectures no supported device runs.
Localisation resources. Strings and assets for every language, delivered to every user.
Duplicated resources. The same asset included by several dependencies, or by both the app and a library.
Debug artefacts. Symbols, test resources, and development configuration that reached the release build.
That final category is worth checking first because it is pure waste. It appears through build configuration mistakes, and finding several megabytes of test fixtures in a release build is a common outcome of a first size audit.
Reducing Download Size
Ordered by effect per unit of effort:
Adopt platform delivery formats. Both major platforms support upload formats where the store generates device-specific downloads containing only the architecture, density, and language a given device needs. This frequently reduces download size by a substantial fraction with no code change, and it is the single highest-return action available.
Convert images to modern formats. Modern codecs achieve considerably smaller files at equivalent quality than older formats. Applied across an image-heavy application the saving is large.
Enable resource shrinking and code stripping. Removing unreferenced resources and unreachable code. Requires care with reflection-based access, which strippers cannot always see, and the tooling supports keep rules for those cases.
Audit dependencies for size. Determine what each library contributes and whether the functionality justifies it. Replacing a large dependency used for one function with a small implementation is frequently worthwhile.
Subset fonts. Include only the weights and character ranges used. For applications supporting non-Latin scripts this can be the largest single saving.
Move rarely-used features to on-demand delivery. Both platforms support downloading feature modules after install. Suitable for features a minority of users reach.
Compress and deduplicate assets. Identify identical resources included more than once.
The first item deserves emphasis because it is largely configuration rather than engineering. Teams that have not adopted device-specific delivery are shipping every architecture and every language to every user, and the correction requires a build configuration change rather than a refactor.
Startup Is Several Distinct Phases
Optimising startup requires knowing which phase is slow, because the interventions differ entirely.
Process creation. The operating system creates the process and loads the runtime. Largely outside your control, and affected by binary size and the number of native libraries to link.
Application initialisation. Your application-level setup runs. This is where most avoidable time accumulates.
First screen construction. Layout inflation, view creation, and initial data loading.
First frame rendered. The user sees something.
Interactive. The user can act.
Two distinctions matter for measurement. A cold start begins with no process, which is the first launch and the worst case. A warm start reuses an existing process and is substantially faster. Optimisation should target cold start, since that is what a new user experiences.
And the metric that matters is time to something useful rather than time to first frame. An application that renders a blank shell quickly and takes four more seconds to show content has optimised the measurement rather than the experience.
What Blocks the First Frame
The specific patterns that delay startup, in rough order of how much time they typically consume:
Synchronous initialisation of every SDK. Analytics, crash reporting, advertising, feature flags, and push notifications all initialised in sequence before the first screen. Each takes tens or hundreds of milliseconds and collectively they dominate.
Network requests on the critical path. Fetching configuration or feature flags before rendering means startup depends on network latency. On a poor connection this is seconds.
Database migration or heavy queries at launch. Schema migration on the main thread blocks everything.
Reading large files synchronously. Configuration, cached data, or preferences read from disk before the first frame.
Deep view hierarchies. Complex layouts take measurable time to construct. Flattening reduces it.
Loading full-resolution images for the launch screen. Decoding a large image is expensive.
Reflection-based dependency injection at startup. Runtime graph construction can be slow; compile-time injection avoids it.
Main thread blocked on locks. Contention during initialisation, which is difficult to diagnose without profiling.
The first two account for most avoidable startup time in most applications. Both are fixable by deferring rather than removing, which means no feature is lost.
Deferring Work Without Breaking It
The general technique is moving work off the startup path while ensuring it still happens.
Initialise lazily on first use. An SDK needed only when a specific screen opens should initialise when that screen opens. Most SDKs support this despite documentation suggesting otherwise.
Initialise after the first frame. Work that must happen but not before rendering can be scheduled immediately after the first frame is presented. This preserves the behaviour and removes it from the perceived startup time.
Move to background threads. Anything not required for the first frame that does not touch the interface. Care is needed around initialisation ordering.
Prioritise the first screen’s data. Load what the visible screen needs; defer everything else.
Cache expensive computation across launches. Work whose result is stable can be computed once and persisted.
Use compile-time dependency injection. Removes runtime graph construction entirely.
Show content progressively. A screen that renders its structure immediately and fills in data as it arrives feels substantially faster than one that waits for everything.
Two cautions. Deferred initialisation must be verified to still happen — an SDK moved off the startup path and accidentally never initialised produces missing analytics that nobody notices for weeks. And ordering dependencies between SDKs are real; deferring one that another depends on causes intermittent failures that are difficult to reproduce.
The safe pattern is an explicit initialisation registry with declared phases, so that what runs before the first frame and what runs after is visible in one place rather than distributed across the codebase.
Measuring on Real Devices
Measurement on development hardware produces conclusions that do not transfer.
Test on a mid-range device several years old. Not the newest flagship. The performance difference is large, and the majority of users are not on flagships.
Measure cold start specifically. Force-stop the application and clear it from memory between measurements. Warm starts are much faster and not what new users experience.
Throttle the network. Startup that depends on a network request behaves entirely differently on a slow connection.
Measure from the store build. Release builds with optimisation enabled differ substantially from debug builds.
Use platform startup metrics from real users. Both platforms report startup timing from actual installs, which is the only representative data. Field data frequently differs from lab measurements by a large margin.
Profile rather than guess. A trace showing where the time goes is worth more than any amount of reasoning about what might be slow.
The gap between lab and field measurement is worth internalising. Teams that optimise against local measurements sometimes find field metrics unchanged, because the bottleneck in the field was network latency or storage speed on cheaper devices — neither of which appears locally.
Preventing Regression
Both size and startup time degrade continuously unless something prevents it, because every feature adds a little.
Size budget enforced in CI. A build exceeding the budget fails, or requires explicit approval. This is the single most effective control.
Size delta reported on every pull request. Making the cost of a change visible at the point of decision changes decisions.
Startup time in automated testing. Measured on a consistent device, tracked over time, with alerts on regression.
Dependency review before adoption. New dependencies evaluated for size contribution as part of the decision.
Periodic audits. Quarterly review of size distribution and startup traces, which catches gradual accumulation that per-change checks miss.
Field metrics monitored. Real-user startup timing and, where available, install conversion by size.
The pull request size delta deserves emphasis because of how it changes behaviour. An engineer who sees that their change adds 1.8 MB will frequently find a smaller alternative. The same engineer, told nothing, has no reason to look.
Common Pitfalls
Measuring on flagship hardware only. Hides the experience most users have.
Measuring warm starts. Much faster and not what new users see.
Optimising time to first frame only. A blank shell rendered quickly is not a faster app.
Network requests before the first frame. Makes startup depend on connection quality.
Initialising every SDK synchronously. Usually the largest single contributor.
Deferring initialisation without verifying it happens. Silently broken analytics.
No size budget. Guarantees gradual growth.
Conclusion
Download size and cold start time determine how many users reach your product at all, and both are usually dominated by a small number of causes.
For size, adopting platform device-specific delivery formats is largely configuration and frequently the largest single reduction available. Then modern image formats, resource shrinking, font subsetting, and an honest audit of what each dependency contributes.
For startup, the dominant costs are synchronous SDK initialisation and network requests on the critical path. Both are deferrable rather than removable, which means no functionality is lost — moving them after the first frame, or to first use, recovers most of the time.
Then measure on a mid-range device several years old, over a throttled connection, from a release build, and cross-check against field metrics from real installs. Local measurements on fast hardware produce conclusions that do not transfer.
And enforce a size budget in CI with the delta reported on every pull request. Both metrics degrade continuously without a mechanism that makes the cost visible at the moment someone is deciding.
Frequently Asked Questions
How much does app size affect install conversion? Measurably, with the effect stronger on slower connections and cheaper devices. The precise relationship varies by category and market, and the direction is consistent enough to treat size as a conversion metric.
What cold start time should be targeted? Under two seconds to useful content on a mid-range device is a reasonable goal. Beyond five seconds, abandonment becomes significant.
Is on-demand feature delivery worth the complexity? For features a minority of users reach, yes. It adds error handling for download failures and a loading state, which is justified when the feature is large and rarely used.
Can SDK initialisation safely be deferred? Usually, and it must be verified. Some SDKs need early initialisation to capture crashes or early events; most do not despite their documentation. Test that deferred initialisation still produces the expected data.
Why do field metrics differ from local measurements? Device variety, network conditions, storage speed, and background load. Field data is the authoritative source; local measurement is for iteration speed.
Does reducing size improve startup time? Somewhat — smaller binaries link faster and fewer resources means less to load. The relationship is real and weaker than the direct startup optimisations.
What is the fastest available improvement? Adopting the platform’s device-specific delivery format, if not already done. It requires build configuration rather than code changes and typically reduces download size substantially.



