majorsenior2023

Turo Android

The 77% Cold Start

A car-sharing app's startup was slow enough to lose bookings. Five separate issues on the main thread. One A/B test proved 77% was recoverable.

The Incident

Turo's Android team used Android Vitals to identify that their cold start was underperforming. Rather than guessing, they profiled methodically and found five independent problems all adding latency to the startup critical path. They validated each fix with an A/B test (50% user split) before shipping — and the final result was a 77% reduction in average cold start time.

Evidence from the Scene

  • A custom SplashActivity with a branded animation played for ~1 second before any home screen work
  • Feature flag service was called synchronously — home screen waited for the server response
  • StrictMode flagged SharedPreferences reads during Application.onCreate()
  • Dagger injected the entire dependency graph at startup including objects used only minutes later
  • Joda Time loaded timezone data via getResourceAsStream() during app initialization
  • No Baseline Profiles shipped — every install started with pure JIT interpretation

The Suspects

3 of these are the real root causes. The others are plausible-sounding distractors.

Custom SplashActivity with animation consuming ~1 second before home screen work began

Synchronous feature flag network fetch blocking the home screen from rendering

Dagger constructing the full dependency graph eagerly at startup — including objects not needed for minutes

R8/ProGuard disabled, causing a bloated binary and slow class loading

Full-resolution vehicle images fetched before the home screen rendered

The Verdict

Real Root Causes

  • Custom SplashActivity with animation consuming ~1 second before home screen work began

    A dedicated SplashActivity that plays a branded animation delays every user's first meaningful render by its full duration. Migrating to the native Android Splash Screen API (API 31+) eliminated the extra Activity hop and reclaimed ~1 second of startup.

  • Synchronous feature flag network fetch blocking the home screen from rendering

    Waiting for a feature flag response before rendering the home screen adds network RTT to every cold start. The fix: fetch asynchronously in a coroutine, render with default values immediately, and update reactively when the response arrives.

  • Dagger constructing the full dependency graph eagerly at startup — including objects not needed for minutes

    Eager DI graph construction means every singleton — payment processor, analytics, video player — is instantiated before the home screen appears. dagger.Lazy<T> defers construction to first use, removing objects from the startup critical path.

Plausible But Wrong

  • R8/ProGuard disabled, causing a bloated binary and slow class loading

    Binary size does affect startup, but the profiler trace pointed to specific blocking operations (splash, network, disk I/O) as the dominant causes — not class loading time from an unoptimized binary.

  • Full-resolution vehicle images fetched before the home screen rendered

    Image loading adds perceived latency but typically runs on background threads via Coil/Glide and doesn't block TTID. The blocking issues here are all on the main thread.

Summary

Turo found five independent problems each contributing to startup latency: a 1-second branded splash, a synchronous feature flag fetch, main-thread SharedPreferences reads, eager Dagger graph construction, and Joda Time's timezone loading. Each was fixed independently and validated with an A/B test. The combined result: 77% reduction in average cold start, 84% at p50. Turo also added Baseline Profiles for a 15% AOT bonus. The case study is a textbook example of disciplined performance debugging — profile first, fix independently, A/B test each change.

The Real Decision That Caused This

Building startup initialization without a 'nothing blocks the main thread' rule, and shipping without validating the startup critical path with StrictMode or Android Profiler before release.

Lesson Hint

Chapter 7 (Platform & Performance) covers startup optimization, Baseline Profiles, and StrictMode. Chapter 2 (App Architecture) covers dependency injection scoping and lazy initialization.

Want to test yourself before reading the verdict?

Open Interactive Case in Autopsy Lab