Compose Performance & Stability Interview Questions

UI › Compose UI

Walk me through what 'skipping' actually means when Compose decides not to recompose a composable, and why that's different from Compose simply not redrawing pixels.

What a strong answer covers: Skipping happens at composition time: when every parameter is stable and equal by == to the previous composition, the runtime skips re-executing the composable function's body entirely rather than executing it and finding nothing changed. This is distinct from layout and draw phases; skipping avoids the composition-phase cost, which matters most when a function is invoked thousands of times in a list. The nuance a weak answer misses: one unstable parameter disables skipping for the whole function even if that specific parameter's value didn't change.

You've profiled a screen and found a LazyColumn item recomposing on every scroll frame even though its underlying data hasn't changed. Walk me through how you'd track down the cause.

What a strong answer covers: First check whether the item reads something that changes every frame directly in its body instead of deferring the read, like computing a value from scroll position inline instead of inside a Modifier.offset { } lambda or derivedStateOf, since that forces recomposition every frame rather than just relayout. If bind logic looks clean, check for an unstable parameter, an unremembered List, a raw lambda, or a data class the compiler can't infer stability for, which forces the whole composable to be treated as always-changed. A strong candidate reaches for the Compose Compiler metrics or Layout Inspector's recomposition counts to confirm the exact composable and parameter before guessing.

Strong Skipping Mode in Compose Compiler 2.0 changed what counts as skippable and how lambdas get memoized. What's the trade-off, and when would a team deliberately opt a module out of it?

What a strong answer covers: Strong Skipping Mode makes unstable-parameter composables skippable too and auto-memoizes lambdas without explicit remember, cutting a lot of boilerplate and unnecessary recomposition. The trap: because more things now skip by default, a composable relying on side-effecting reads in its body rather than tracked snapshot state can silently stop updating when it previously recomposed 'by accident', and auto-memoized lambdas capturing mutable non-state variables can go stale since the same lambda instance gets reused across recompositions. Teams opt a module out or mark specific composables non-skippable when they have intentional non-snapshot side effects on every call, or when incrementally migrating and want to isolate behavior changes before rolling out app-wide.

Back to Compose Performance & Stability