Skip to content

Stability Reality Check

The Stability Reality Check answers a question static analysis alone cannot: does the compiler's stability prediction actually hold up at runtime? Since Compose's strong skipping compares unstable parameters by instance identity (===) and stable ones by equals(), a parameter the compiler flags "unstable" frequently skips perfectly fine in practice — while another quietly recomposes on every frame. This feature joins the static prediction (from in-IDE analysis) with live recomposition data (from the heatmap stream) and grades each parameter accordingly.

reality-check

Prerequisites

Reality Check reuses the heatmap's live data pipeline, so it shares the same requirements:

  1. The Compose Stability Analyzer Gradle plugin applied to your project.
  2. Composable functions annotated with @TraceRecomposition.
  3. Logging enabled via ComposeStabilityAnalyzer.setEnabled(true) in your Application class.
  4. A device or emulator connected via ADB, with the Recomposition Heatmap started.

The Grades

Each parameter of a traced composable is graded by reconciling its compile-time prediction with what actually happened at runtime:

Grade Meaning What to do
🟢 Confirmed Predicted stable and it behaves stably — no wasteful recomposition. Nothing.
🟡 False alarm Predicted unstable, but the instance stays referentially stable at runtime, so Compose skips it fine. Safely ignore the "unstable" warning.
🔴 Silent waste The value is equals-equal but arrives as a new instance every recomposition, so strong skipping's === check fails and it recomposes when it could have skipped. Hoist the value into remember, or make the type stable.
Justified The value genuinely changes between recompositions, so recomposing is necessary. Nothing — correct behavior.
Observing Not enough recompositions observed yet to grade. Interact with your app a few more times.

Why strong skipping makes this necessary

Before strong skipping, an unstable parameter always made a composable non-skippable. With strong skipping (Compose Compiler 2.0+), unstable parameters are compared by identity (===), so the same instance skips fine — the compiler's "unstable" label is often a false alarm. The real cost is a fresh-but-equals-equal instance every frame, which this feature surfaces as silent waste.

Where Grades Appear

  • Editor inlay: the block above each composable shows the recomposition count plus a grade summary (e.g. (!) silent waste: user).
  • Hover tooltip: hovering a composable adds a "predicted vs. actual" column to the stability table, with a per-parameter grade badge.
  • Reality tab: the Reality tool-window tab is a module-wide scorecard listing each observed composable, its per-parameter grades, and a "wasted recompositions (recent)" tally. Double-click a row to jump to the source.

How to Use

  1. Annotate the composables you want to grade with @TraceRecomposition.
  2. Start the Recomposition Heatmap and interact with your app to drive recompositions.
  3. Hover a composable, watch its inlay, or open the Reality tab — grades appear once a parameter has been observed a few times (the observation threshold filters out early noise).

Grading needs observed recompositions

A grade is only assigned after a composable actually recomposes a few times. Because the recomposition tracker runs inside the composable body, a parameter that simply skips is confirmed as expected and is not re-tracked — that's the goal, not a problem.