Use this skill to configure R8 correctly for a Jetpack Compose application — full mode by default, `proguard-android-optimize.txt`, resource shrinking on, and minimal keep rules because Compose ships consumer ProGuard rules. Covers AGP 8.0+ R8 full mode default, R8's Compose-aware optimizations (lambda grouping, `sourceInformation()` stripping, composable arg constant-folding, `ComposerImpl` devirtualization), legitimate keep needs (`@Serializable`, Hilt entry points, reflective `Saver`s), and the AGP 8.x missing-rule reporter / R8 retrace. Cited gain is roughly 75 percent startup and 60 percent frame-render improvement debug-to-release. Use when setting up a new Compose app, when a PR adds an over-broad keep like `-keep class androidx.compose.** { *; }`, when a release build crashes after enabling minification, when APK size needs reduction, or when first enabling minification.
npx skills add https://github.com/skydoves/compose-performance-skills --skill configuring-r8-for-compose
R8 is the only supported shrinker — ProGuard is deprecated. Compose ships consumer ProGuard rules, so most apps need no Compose-specific keep rules. The big modern wins come from R8 full mode (default since AGP 8.0): lambda grouping, sourceInformation() stripping, constant-folding of composable args, and ComposerImpl devirtualization. This skill teaches Claude to set R8 up minimally and refuse over-broad keeps that defeat those wins.
-keep class androidx.compose.** { *; } or any other Compose-wide keep — push back and apply this skill.classes.dex and the developer wants to know why.isMinifyEnabled = true is being flipped on, before any release benchmark is run.../../measurement/testing-compose-in-release-mode/SKILL.md whenever release measurement is being set up.ClassNotFoundException, NoSuchMethodError, or empty reflection results — not behavioral bugs.stability/ or recomposition/ skills.gradle.properties with android.enableR8.fullMode=true.../../measurement/testing-compose-in-release-mode/SKILL.md for measurement signing).org.jetbrains.kotlin.plugin.compose plugin so Compose's bundled consumer rules are wired correctly through dependency resolution.proguard-android.txt (no R8 optimizations) and proguard-android-optimize.txt (R8 optimizations on).Surface these to the developer when they ask "why bother" or push back against minification:
sourceInformation() stripping. Each composable emits a string of source-position metadata. R8 removes it from release builds — smaller APK and less work per recomposition.if (changed and 1 == 0) style checks the recomposer can short-circuit.ComposerImpl devirtualization. R8 devirtualizes hot calls into Compose's runtime, which is a major contributor to the cited frame-render improvement.isShrinkResources = true, R8 strips unreachable drawables, strings, and layout XML.Cited measurement: roughly 75 percent startup gain and 60 percent frame-render gain debug to release. Source: Ben Trengrove, "Why should you always test Compose performance in release" (Android Developers Medium).
// app/build.gradle.kts
android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
}
proguard-android.txt instead of proguard-android-optimize.txt. Only the -optimize variant enables R8's optimization passes.androidx.compose.runtime, androidx.compose.ui, androidx.compose.foundation, androidx.compose.material3, etc.) ships a consumer-rules.pro that R8 picks up automatically. The developer's proguard-rules.pro should contain no lines starting with -keep class androidx.compose..@Serializable data classes consumed by kotlinx.serialization (the kotlinx-serialization Gradle plugin emits the right rules — verify, do not duplicate).rememberSaveable Saver objects only when accessed reflectively from outside the module.Class.forName(...) consumer, JNI binding, or service loader.app/build/outputs/mapping/release/missing_rules.txt listing the exact -keep directives R8 inferred from runtime failures. Add only those, narrowed to the specific class/method.# Modern R8 retrace (cmdline-tools 7.0+):
$ANDROID_HOME/cmdline-tools/latest/bin/retrace \
app/build/outputs/mapping/release/mapping.txt < crash.txt
Or use the Gradle convenience target wired into AGP:
./gradlew :app:retraceR8DebuggingArtifact
(The legacy ProGuard retrace.sh install path under $ANDROID_HOME/tools/ was removed when SDK Tools 26 was sunset; do not look for it.) Always retrace before diagnosing a release crash — the stack frames are otherwise meaningless.
./gradlew :app:assembleRelease
# Then in Android Studio: Build → Analyze APK → app-release.apk
# Compare classes.dex method count and resources.arsc size against the same build with isMinifyEnabled = false.
A correctly configured Compose app with no over-broad keeps typically halves the method count vs the un-minified release.
../../measurement/testing-compose-in-release-mode/SKILL.md. The expected delta vs CompilationMode.None against a debug variant is in the 75 percent / 60 percent range cited above.# WRONG
-keep class androidx.compose.** { *; }
# WRONG because: Compose's own consumer rules are tighter and correct; a wildcard keep blocks lambda grouping, prevents sourceInformation stripping, kills composable-arg constant folding, defeats ComposerImpl devirtualization, and bloats classes.dex. The cited 75/60 gains evaporate.
# RIGHT — let Compose's consumer rules handle it.
# (no Compose-specific lines in proguard-rules.pro by default)
// WRONG
buildTypes.release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro",
)
}
// WRONG because: proguard-android.txt explicitly skips R8's optimization passes. Lambda grouping, devirtualization, constant folding, sourceInformation stripping all do not run. The release variant builds and installs but performs like a poorly minified app.
// RIGHT
buildTypes.release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
kotlin.Metadata keep# WRONG
-keep class kotlin.Metadata { *; }
# WRONG because: blanket-keeping kotlin.Metadata blocks R8 from shrinking metadata for unreachable Kotlin classes; identify the actual reflective consumer (kotlinx.serialization, Moshi, Gson, Hilt) and add a narrow rule for that consumer instead.
# RIGHT — narrow to the reflective consumer
# Example: kotlinx.serialization needs Companion access on KSerializer implementations.
-keepclassmembers class * implements kotlinx.serialization.KSerializer {
public static **$Companion Companion;
}
-dontobfuscate for "easier debugging"# WRONG
-dontobfuscate
-dontoptimize
# WRONG because: turning off obfuscation and optimization on the release variant removes most of R8's benefit; the variant being measured is no longer the variant that ships. If readable stacks are needed, ship with R8 on and use retrace + the mapping.txt to deobfuscate.
# RIGHT — leave R8 on, keep mapping.txt for retrace.
# (no -dontobfuscate / -dontoptimize lines)
# Upload mapping.txt to your crash reporter (Crashlytics, Sentry, etc.) so stacks
# are deobfuscated automatically; or run R8 retrace manually:
# $ANDROID_HOME/cmdline-tools/latest/bin/retrace mapping.txt < crash.txt
# (or: ./gradlew :app:retraceR8DebuggingArtifact)
@Serializable keep when the kotlinx-serialization plugin is missing# RIGHT — when using kotlinx.serialization with reflection-based polymorphism
-keepclassmembers class * implements kotlinx.serialization.KSerializer {
public static **$Companion Companion;
}
-keepclasseswithmembers class * {
@kotlinx.serialization.Serializable <init>(...);
}
# But PREFERRED: apply the kotlinx-serialization Gradle plugin so these rules are emitted automatically.
rememberSaveable Saver# WRONG — defensive blanket keep
-keep class **.Saver { *; }
# WRONG because: most Saver implementations are reachable from non-reflective code and are kept automatically; a blanket keep prevents inlining and elimination.
# RIGHT — only when the Saver is loaded by name reflectively
-keepclassmembers class com.example.feature.MyTypeSaver {
public static *** INSTANCE;
}
proguard-android-optimize.txt, not proguard-android.txt. The non-optimize variant disables R8's optimization passes.isMinifyEnabled = true AND isShrinkResources = true for release.-keep class androidx.compose.** { *; }, -keep class * extends androidx.compose.runtime.Composable, etc.). Compose ships correct consumer rules; a blanket keep defeats lambda grouping, sourceInformation stripping, constant folding, and ComposerImpl devirtualization.-dontobfuscate or -dontoptimize to the release variant for "easier debugging". Measure with R8 on and use R8 retrace + mapping.txt for stack readability; debug code paths separately on the debug variant.missing_rules.txt reporter to find narrow under-keeps. Do not pre-emptively keep packages.mapping.txt before drawing conclusions about the cause.kotlinx-serialization, Hilt, Moshi-codegen). The plugins emit the right rules automatically.Build → Analyze APK or a Gradle plugin so a regression in dex size flags an over-broad keep added by a future PR.proguard-rules.pro contains zero lines matching ^-keep .* androidx\.compose\..app/build.gradle.kts release block uses getDefaultProguardFile("proguard-android-optimize.txt") exactly.isMinifyEnabled = true and isShrinkResources = true are both set on the release buildType../gradlew :app:assembleRelease succeeds.classes.dex method count is meaningfully lower than the same release built with isMinifyEnabled = false.CompilationMode.Partial(BaselineProfileMode.Require) against the release variant is faster than the same benchmark with CompilationMode.None.app/build/outputs/mapping/release/mapping.txt.missing_rules.txt was generated, only the listed narrow keeps were added — no broadening.Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification
Take skydoves/configuring-r8-for-compose from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.