Startup time is the first thing every user experiences and one of the few mobile metrics with a clear link to retention. It is also widely mismeasured: teams optimise the part they can see in their own code while the majority of a cold start happens before any of their code executes.
01The three starts are different problems
A cold start means the process does not exist: the system loads your binary, initialises the runtime, and only then runs your code. A warm start reuses a process that is still resident. A hot start is returning to an app already in memory.
Users experience all three but complain about cold starts, which are also the hardest to improve. Optimising warm start feels productive and moves almost nothing that matters.
Measure them separately or your average hides the problem. A p50 that looks fine is often a mix of fast hot starts and slow cold starts, and the cold-start p90 is what drives abandonment.
02Where cold start time actually goes
Before your first line runs, the system has loaded and linked your binary. That cost scales with binary size and the number of dynamic frameworks you link, which is why dependency count matters more than dependency quality here.
Then the runtime initialises. On React Native this includes loading and parsing the JavaScript bundle; on Flutter it is engine initialisation. Bundle size directly translates to parse time on low-end devices, where it can dominate everything else.
Only then does your initialisation run - and this is where teams put analytics SDKs, crash reporters, feature flag fetches and remote config, often serially and often blocking first paint.
- Audit what runs before first paint. Most SDK initialisation can be deferred by a few hundred milliseconds with no functional difference.
- Never block launch on a network call. Feature flags and remote config must have local defaults that render immediately.
- Lazy-load screens that are not the launch screen; route-level code splitting matters as much on mobile as on web.
- Cut linked frameworks you no longer use - each one costs load time on every launch forever.
- Measure on the oldest device you support, not the newest. The gap is often three to five times.
03What users actually perceive
Perceived start matters more than measured start. An app that shows meaningful structure quickly feels faster than one that shows a spinner for less total time.
Render the shell - navigation, headers, skeleton content - before data arrives, and populate as it lands. This does not reduce time-to-interactive but substantially reduces abandonment, because the user can see the app working.
Avoid the pattern where a splash screen is followed by a blank screen followed by content. That second gap reads as a failure even when total time is short.
04Setting a target
Under two seconds cold on a mid-range device is a reasonable target for most apps; under a second is achievable for simple ones and rarely worth the effort beyond that.
The more useful discipline is a regression budget in CI. Startup time degrades gradually as dependencies accumulate, and without a gate it will drift by a second over a year with no single commit to blame.
Topics
Aarav Patel
Principal Engineer · SyncTrix
Writes about the engineering decisions behind production systems - architecture, delivery and the trade-offs that only show up at scale.
Building something like this?
SyncTrix engineers AI, SaaS, platform and cloud systems for enterprises and high-growth teams. Tell us what you're shipping and we'll scope it with you.
Talk to an engineer