auditing-compose-performance
Auditing Compose Performance — the Measure → Diagnose → Fix → Verify orchestrator
This is the highest-level entry point in the compose-performance-skills library. When the developer's symptom is broad — "the app feels sluggish", "scroll is rough everywhere", "we're starting a perf sprint", "where do we even start?" — Claude enters here. The orchestrator does NOT replace the 25 focused skills; it sequences them through a four-phase loop and produces a written audit report at the end.
The phases are Measure → Diagnose → Fix → Verify, run in order, never skipped. Phase 1 establishes baseline numbers from a release + R8 build on a real device, because anything else is fiction. Phase 2 turns symptoms into named causes from the Compose Compiler reports, Layout Inspector, and runtime tracing. Phase 3 applies one targeted fix at a time, re-measuring between fixes so the delta of each change is provable. Phase 4 regenerates the Baseline Profile, locks in a CI stability gate, and commits the baseline files so regressions cannot slip back.
Perf work without measurement is guessing. Skydoves hot take #1 applies throughout: DO NOT chase 100% skippability — that is a diagnostic on a compiler report, not the goal. The goal is FrameTimingMetric and StartupTimingMetric improvement on a real device.
When to use this skill
- The developer reports broad sluggishness with no specific surface ("the app feels heavy", "everything is slow").
- A team is kicking off a performance sprint and wants a structured plan.
- A new team member needs onboarding to a perf-troubled codebase.
- A pre-release perf gate is required before shipping.
- The user asks for an "audit", "perf review", or a written deliverable.
- The user has no idea where to start and wants Claude to triage.
When NOT to use this skill
More from skydoves/compose-performance-skills
collecting-flows-safely
Use this skill to migrate Compose UI from `collectAsState()` to `collectAsStateWithLifecycle()`, hoist `Flow<T>` parameters out of composables, and apply `.conflate()` / `.distinctUntilChanged()` / `snapshotFlow` so background CPU and battery stop draining and chatty flows stop invalidating the UI per emission. Covers ViewModel `StateFlow`/`SharedFlow` consumers, sensor and location streams, and the "Flow as composable parameter" antipattern. Trigger when the user mentions `collectAsState`, `collectAsStateWithLifecycle`, lifecycle-aware flow collection, `Lifecycle.State.STARTED`, background battery drain from a Compose screen, `snapshotFlow`, `Flow` parameter on a composable, conflate, or distinctUntilChanged.
10debugging-recompositions
Use this skill to find which Jetpack Compose composables are recomposing and why, using Android Studio Layout Inspector recomposition counts and skip counts, the per-parameter Argument Change Reasons (Changed / Unchanged / Uncertain / Static / Unknown) introduced in Android Studio Hedgehog and later, and runtime `@TraceRecomposition` from `compose-stability-analyzer` for production-like measurement. Walks through enabling counts, mapping each Argument Change Reason to a fix, and confirming the result in a release build. Use when the developer says "this should be skipping but isn't", "I want to see recomposition counts", asks what "Uncertain" or "Unknown" means in the inspector, or needs to confirm a stability or strong-skipping fix actually worked end-to-end.
10configuring-lazy-prefetch
Use this skill to tune Jetpack Compose lazy-layout prefetch with LazyLayoutCacheWindow (Compose Foundation 1.9+, @ExperimentalFoundationApi) and pausable composition in prefetch (Compose Foundation 1.10+, default on). Covers configurable Dp-based ahead/behind cache windows plumbed through rememberLazyListState(cacheWindow = ...), NestedPrefetchScope for items containing inner lazy layouts (HorizontalPager inside a LazyColumn row), version requirements, and the trade-off between memory pressure and idle-frame work. Use when the developer mentions dropped frames at high scroll velocity, prefetch window, ahead/behind extents, LazyLayoutCacheWindow, NestedPrefetchScope, pausable composition for prefetch, or wants composition retained for items briefly scrolled past. Item-level fixes (keys, contentType) live in a sibling skill.
9optimizing-lazy-layouts
Use this skill to fix scroll jank, lost item state, and broken animateItem() animations in LazyColumn, LazyRow, LazyVerticalGrid, and LazyHorizontalGrid. Covers stable item keys, contentType for mixed-type feeds, Modifier.animateItem() requirements, hoisting modifier chains and painters out of the items lambda, and validating item composable stability. Use when the developer mentions LazyColumn jank, dropped frames while scrolling, items losing scroll state on insert/remove/reorder, mixed feeds of cards/headers/ads feeling sluggish, animateItem() not animating, RecyclerView view-type analog, key parameter, or contentType parameter. The prefetch-window tuning lives in a sibling skill.
9stabilizing-compose-types
Use this skill to fix unstable Jetpack Compose types once a stability diagnosis has identified them. Covers the three-tier strategy — make the type truly stable with val plus immutable fields, mark with @Immutable or @Stable when the source is owned, and use stabilityConfigurationFiles for third-party or Java types. Explains the compiler-level difference between @Immutable and @Stable (static expression promotion), kotlinx.collections.immutable for List/Set/Map parameters, and the StableHolder wrapper escape hatch. Use when the developer asks how to stabilize a User class, a List parameter, java.time.LocalDateTime, a Flow parameter, or when the compiler report shows unstable params and the developer wants the fix. The diagnostic step lives in a sibling skill.
9migrating-to-modifier-node
Use this skill to author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T>. Covers the persistent-node lifecycle (onAttach, onDetach, onReset, coroutineScope), the specialized node interfaces (DrawModifierNode, LayoutModifierNode, SemanticsModifierNode, PointerInputModifierNode, CompositionLocalConsumerModifierNode, LayoutAwareModifierNode, GlobalPositionAwareModifierNode, ObserverModifierNode, DelegatingNode, TraversableNode), why ModifierNodeElement MUST be a data class for diffing, and the manual-invalidation knobs (invalidateDraw, invalidateMeasurement, invalidatePlacement, shouldAutoInvalidate). Use when the developer mentions Modifier.composed, custom modifier, ModifierNodeElement, Modifier.Node, "rewriting our drawBehind helper", node lifecycle, or sees Modifier.composed { } in a code review.
9