$npx -y skills add skydoves/compose-performance-skills --skill using-strong-skipping-correctlyUse this skill to reason about Jetpack Compose's Strong Skipping Mode — the default since Kotlin 2.0.20 — including what it changes about skippability, when it does and does not auto-remember lambdas, and which escape hatches (@DontMemoize, @NonSkippableComposable, `@NonRes
| 1 | # Using Strong Skipping Correctly — make every restartable composable skippable, intentionally |
| 2 | |
| 3 | Strong Skipping Mode is the Compose compiler behavior that became the default with the Kotlin Compose compiler plugin shipped in **Kotlin 2.0.20**. It changes two things at once: every restartable composable becomes skippable regardless of param stability (unstable params are compared with `===`, stable params with `equals`), and every **capturing** lambda literal written inside a `@Composable` function is automatically wrapped in `remember(captures) { ... }`. Both behaviors are on by default; neither requires opt-in flags on Kotlin 2.0.20+. Lambdas with no captures are already compiler-emitted singletons and are NOT wrapped — only capturing lambdas need (and get) the auto-`remember` treatment. |
| 4 | |
| 5 | That sounds like "stability no longer matters", and for a lot of UI code it almost is. But the change leaves three sharp edges: stable types still need correct `equals` semantics so the `===` fallback does not invalidate every recompose, lambdas in non-@Composable scopes (`LazyListScope.items { }`, `Modifier.pointerInput { }`, plain object expressions) are not auto-memoized, and a few intentional cases need explicit opt-out via `@DontMemoize` or `@NonSkippableComposable`. This skill walks the verification, audit, and escape-hatch decisions for working in a strong-skipping world. |
| 6 | |
| 7 | ## When to use this skill |
| 8 | |
| 9 | - The developer asks "do I still need `@Stable`?" or "does this composable skip now?". |
| 10 | - A composable still recomposes on every parent tick even though the developer "thought strong skipping fixed that". |
| 11 | - A lambda allocation question comes up — "is this `onClick = { vm.add(it) }` allocating per recompose?". |
| 12 | - Migrating a project from Kotlin 1.9.x / Compose Compiler 1.5.x to Kotlin 2.0.20+ and reconciling reports that now show `restartable skippable` everywhere. |
| 13 | - The developer encounters `@DontMemoize` or `@NonSkippableComposable` in code or compiler output and asks what they do. |
| 14 | - The user mentions "strong skipping", "auto-remember", "@DontMemoize", "@NonSkippableComposable", or "skippable everywhere". |
| 15 | |
| 16 | ## When NOT to use this skill |
| 17 | |
| 18 | - The conceptual question is "what makes a type stable in the first place?" — use `../../stability/understanding-stability-inference/SKILL.md`. |
| 19 | - Stability is regressing silently and the team needs a CI gate — use `../../stability/enforcing-stability-in-ci/SKILL.md`. |
| 20 | - Even with strong skipping on, type-level stability still matters for diagnostic clarity and for `equals`-based skipping to work — use `../../stability/diagnosing-compose-stability/SKILL.md` to fix the underlying types. |
| 21 | - The recomposition is happening but the developer cannot see it — use `../debugging-recompositions/SKILL.md` to instrument first. |
| 22 | |
| 23 | ## Prerequisites |
| 24 | |
| 25 | - Kotlin **2.0.20+** with the Compose compiler Gradle plugin applied via `org.jetbrains.kotlin.plugin.compose`. Strong Skipping is on by default. To turn it **off** on Kotlin 2.0.20+, use `composeCompiler { featureFlags.add(ComposeFeatureFlag.StrongSkipping.disabled()) }` (the legacy `enableStrongSkippingMode = true` property is `@Deprecated("Use the featureFlags option instead")` and still works on older Kotlin). |
| 26 | - Compose Compiler reports turned on for the module under audit (see `../../stability/diagnosing-compose-stability/SKILL.md`). |
| 27 | - Builds running in **release** when reading reports — debug adds Live Literals which distort skip behavior (see `../../measurement/testing-compose-in-release-mode/SKILL.md`). |
| 28 | - Familiarity with the per-composable line format in `*-composables.txt` (`restartable skippable fun Foo(...)`). |
| 29 | |
| 30 | ## Workflow |
| 31 | |
| 32 | ### 1. Verify strong skipping is actually active |
| 33 | |
| 34 | Before claiming any behavior is "because of strong skipping", confirm the toolchain is on a version where i |