DEV Community

137Foundry
137Foundry

Posted on

How to Set Up Production Cold Start Monitoring Before Users Complain

Most teams find out their cold start time regressed the same way: a wave of one-star reviews mentioning "app takes forever to open," weeks after the change that caused it shipped. By the time that feedback loop closes, the regression has already been live for an entire release cycle. Here's how to catch it before that happens instead.

Step 1: Decide what "cold start" means for your app specifically

Before instrumenting anything, write down a precise definition. Most teams settle on: time from process creation (the OS spawning your app's process from a fully terminated state) to the point where the primary screen is both rendered and interactive, not just visually complete. If your app has meaningfully different launch paths (a deep link into a specific screen versus opening from the home screen icon), decide whether you're tracking them together or separately, because they can have very different profiles.

Step 2: Instrument the start and end timestamps

On Android, mark the start timestamp as early as possible in your Application class, ideally the very first line of onCreate(). On iOS, mark it at the very start of application(_:didFinishLaunchingWithOptions:). Mark the end timestamp at the point your primary screen's content is both rendered and its interactive elements are wired up, which per the interactivity discussion above should be your target metric, not just first paint.

// pseudocode, both platforms follow the same shape
val startTime = SystemClock.elapsedRealtime()
// ... app initialization ...
// once the first screen is rendered AND interactive:
val coldStartDuration = SystemClock.elapsedRealtime() - startTime
analytics.recordMetric("cold_start_duration_ms", coldStartDuration)
Enter fullscreen mode Exit fullscreen mode

Step 3: Distinguish cold, warm, and hot starts in your data

A "warm start" (process already exists, activity recreated) and a "hot start" (app just resumed from background) have very different expected durations from a true cold start, and mixing them into one metric produces misleading averages. Tag every recorded duration with which type of start it was, so your dashboards can filter to true cold starts specifically, which are the worst case and the one most correlated with user-reported "slow to open" complaints.

Step 4: Pick a monitoring platform and wire it up

Sentry's mobile performance monitoring, Firebase's performance monitoring product, and several other platforms can automatically capture app start metrics with minimal manual instrumentation, in addition to whatever custom timestamps you add for interactivity specifically. Most of these tools give you percentile breakdowns (p50, p90, p95) out of the box, which matters because a single average number hides the tail of users on old devices or bad networks who are having a genuinely worse experience than everyone else.

Step 5: Set alerting thresholds, not just dashboards

A dashboard nobody looks at doesn't catch a regression. Set an alert on the p90 cold start duration that fires when it moves more than some threshold (10 to 15 percent is a reasonable starting point) above its trailing baseline, checked daily against the previous week's rolling average. This catches regressions within a day or two of a bad release going out, instead of weeks later via app store reviews.

Step 6: Segment by device tier and OS version

Aggregate numbers hide device-specific regressions. A change that's neutral on average can still be a serious regression specifically on low-memory devices or older OS versions, if it happens to interact badly with how those devices handle memory pressure or background process limits. Segmenting your dashboard by device tier (you can bucket by RAM or by a simple performance-class heuristic) surfaces these regressions that an aggregate p90 number would average away.

Step 7: Correlate regressions with releases automatically

Tag every recorded cold start metric with the app version and, ideally, the build number. When your alerting fires, the first question is always "what changed," and having the version tag already in the data means you can immediately narrow the search to what shipped in that release instead of manually cross-referencing release dates against a metrics dashboard.

Step 8: Review the trend monthly even without an alert

Alerts catch sudden regressions. They don't catch slow drift, the kind that happens when a new SDK gets added every few months and each one nudges the baseline up by thirty or forty milliseconds without ever crossing an alert threshold in a single release. A monthly look at the six-month trend line catches this pattern before it becomes a full second of accumulated, unexplained slowdown.

Step 9: Don't let the dashboard become the whole strategy

A monitoring setup is only as good as the process wrapped around it. We've seen teams build exactly the dashboard described above, get genuinely useful alerts for a few months, and then quietly start ignoring them once the on-call rotation changes and nobody remembers why the alert exists or what threshold it's tuned to. Document the reasoning behind your alert thresholds somewhere durable, not just in the monitoring tool's config, so a new team member can understand why the number is what it is without having to reconstruct the original investigation.

Step 10: Treat a cold start regression with the same urgency as a crash spike

A subtle but important cultural point: teams that already have mature incident response for crash rate spikes often don't extend the same seriousness to a cold start regression, because it doesn't page anyone and doesn't show up as a hard error anywhere. But a cold start regression that pushes launch time from 1.8 to 2.6 seconds is a real product regression, it's just one that shows up in reviews and uninstalls over the following weeks rather than in an error tracker immediately. Alerting can be configured to treat a performance regression with the same severity tier as a crash spike, which is worth doing deliberately rather than leaving performance alerts as a lower-priority notification channel that gets checked less often. Android's vitals dashboard in the Play Console surfaces a similar signal at the store level, worth cross-checking against your own instrumentation periodically to make sure the two sources agree.

A minimal version if you're starting from nothing

If none of this exists yet and the idea of building all ten steps feels like too much to take on at once, the minimum viable version is smaller than it looks: instrument the two timestamps from step two, log them to whatever analytics pipeline you already have (it doesn't need to be a dedicated performance tool on day one), and manually check the median and a rough p90 once a week. That alone catches the worst regressions, and it's an afternoon of work rather than a quarter-long project. Build out the alerting and segmentation later, once the basic instrumentation has proven its value and someone on the team has started actually looking at the numbers regularly.

What good monitoring buys you

The point of all this isn't the dashboard itself, it's the ability to catch a regression in the release that caused it rather than discovering it from user complaints three releases later, at which point untangling which of several shipped changes is actually responsible becomes a much harder investigation. Teams that treat cold start as a continuously monitored metric, the same way they'd treat crash-free rate, consistently ship faster apps than teams that only check it during an occasional manual audit.

For the fixes to pair with this monitoring setup, once you've caught a regression, 137Foundry's guide on reducing cold start time walks through the specific bottlenecks worth checking first, from SDK initialization order to binary size to network calls blocking the first render.

More on how this engineering team approaches production performance work across client codebases is at 137foundry.com.

Top comments (0)