How would you set and enforce a cold-start budget for a React Native app team so startup time stops creeping up release after release?
answer
- define 'interactive' precisely
- release builds, reference low-end Android
- median and slow percentile, many runs
- watch entry imports and bundle growth
- an agreed response when over budget
basics
~20 sDefine cold start to interactive precisely, measure it on release builds on a reference mid-range Android device, set per-platform budgets with phase breakdowns, and guard the usual React Native regressions: entry-file imports, import-time work, bundle growth and network waits.
solid answer
~50 sI would start by agreeing what "interactive" means - for a supermarket app, the home screen showing cached offers and responding to a tap - and measure it on **release builds** on a named **reference mid-range Android device** plus a baseline iPhone, as the median and a slow percentile over many cold starts. Budgets are per platform and split into phases (native start, JavaScript evaluation, first render), so a regression points at an owner. Then guard the React Native-specific ways startup creeps up: new dependencies imported from the entry path, import-time SDK initialisation, bundled data growing the bundle, screens added as static imports, and the home screen or splash waiting on the network. Cheap guards: record bundle composition on every build, review changes to the entry file and root navigator, make screens lazy by default, and check startup before release. Finally, agree the response when the budget breaks - fix, revert or consciously raise it.
go deeper
Know that startup time can be tracked as a number and that it is measured on release builds on real devices.
Explain which everyday changes slow React Native startup - static screen imports, import-time work, bundled data, network waits - and how to spot them in review.
Show you can build the measurement: precise definition, reference devices, repeated cold starts, phase breakdowns and per-build bundle tracking.
Own the policy: budgets per platform, enforcement layers weighed by cost and noise, the agreed response when a budget breaks, and when raising it is legitimate.
## Why startup needs a budget, not a one-off fix Startup rarely regresses in one dramatic change. It creeps: a new analytics SDK here, a statically imported screen there, a bundled JSON file that grows every season. Each change looks harmless in review, and after a year a 1.5 s cold start is 4 s. A **budget** turns "startup is slow" into a number the team owns, and it is only useful if it is measured the same way every time and checked before release. There is no single right design. What follows is a defensible default and the trade-offs a lead should weigh. ## Define the number precisely - **What is measured**: cold start to interactive - app not running, tap the icon, until the first real screen shows meaningful content and responds to input. Decide whether cached content counts as meaningful (it usually should). - **Where**: **release builds** only, since development mode distorts JavaScript cost. Pick a **reference device** per platform, typically a mid-range Android phone representative of your slowest large user segment, plus a baseline iPhone. - **How many runs**: cold-start times vary, so record many runs and track the **median** and a **slow percentile**, not a single measurement. - **Phases**: native start and React Native initialisation, JavaScript evaluation, first render, first data. Phase budgets make a regression point at a likely cause and owner. ## Guard the regressions React Native teams actually hit | Regression source | Guard | |---|---| | A dependency imported from the entry path | review changes to the entry file and root navigator; record bundle composition per build | | Import-time side effects (SDK init, index building) | a review rule: no top-level work in modules reachable at startup | | New screens added as static imports | lazy screens by default via `getComponent` | | Bundled data growing (catalogs, locales) | track bundle size and composition over time, for example with Expo Atlas output | | Splash or home waiting on the network | a documented list of what may block the first screen | | Android packaging drift | keep the uncompressed bundle default unless a decision says otherwise | | Framework and SDK upgrades | re-measure startup as part of every upgrade | ## Enforcement options and their costs 1. **Per-build bundle tracking**: cheap, automatic, catches size growth early. It does not catch evaluation or network regressions. 2. **Automated cold-start runs on real devices**: the most faithful signal, but real devices are noisy and device infrastructure costs money and maintenance. Wide tolerances or many runs are needed to avoid false alarms. 3. **Manual pre-release check** on the reference device: cheap and realistic, but slow feedback and easy to skip under deadline pressure. 4. **Production telemetry** of startup time: shows real users and devices, but arrives after release. Many teams combine cheap per-build checks with a pre-release device measurement and production monitoring, and add automated device runs once the app is large enough to justify them. ## What happens when the budget breaks A budget without a response is a dashboard nobody reads. Agree in advance: - **Who is notified** and who owns the investigation. - **The default response**: fix before release, revert the change, or ship with an explicit, time-boxed exception. - **How the budget changes**: raising it is allowed, but as a recorded decision with the reason, not a quiet drift. ## Trade-offs a lead should name - **Strictness vs noise**: tight thresholds catch small regressions but create false alarms on noisy devices. - **Feature pressure vs startup**: some features justify startup cost; the budget makes the cost visible so it is a choice. - **Reference device choice**: too fast hides problems; too slow makes the budget unattainable and ignored. - **Platform differences**: Android and iOS have different startup profiles, so one shared number is usually wrong. - **Perceived vs measured**: a fast splash hide with skeleton content can feel quicker than a slower screen that arrives complete.
- Why budget per phase rather than one total cold-start number?A total says startup regressed; a phase says where. If JavaScript evaluation grew, look at new imports and side effects; if first data grew, look at what the home screen awaits. Phase budgets route the regression to an owner and shorten the investigation.
- What is the risk of choosing a flagship phone as the reference device?It hides regressions that users on slower Android devices feel strongly, so the budget stays green while real startup worsens. The reference should represent a large, slower segment of your actual users.
saying these in an interview costs you the question
- One cold-start measurement per release is enough to track startup
- Debug builds are fine for startup budgets because they are slower
- A single budget number should cover both Android and iOS
- Bundle size tracking alone catches every startup regression
- A budget that breaks should simply be raised to the new value