R8 234 reads ProGuard 11,741 rule syntax. The rules you need are about code R8 can't see being used: classes loaded by name through reflection, JNI methods, classes serialized with Gson 24,235 or Moshi 10,164 's reflection mode, and anything named in a string. Well-maintained libraries (Retrofit 43,944 , OkHttp 47,077 , kotlinx.serialization 5,947 , Room 234 , Hilt, Compose 234 ) ship consumer rules inside their AARs, which is why BookNest's release build worked with no rules of its own. Since AGP 9.3 your rules can live in files ending in .keep under src/<variant>/keepRules/, next to the code they protect:
# Report in the build output why R8 keeps this class
-whyareyoukeeping class com.example.booknest.data.Book
# Release builds drop debug and verbose logging: R8 may delete calls to these methods
-assumenosideeffects class android.util.Log {
public static int d(...);
public static int v(...);
}-assumenosideeffects tells R8 a call does nothing worth keeping, so it deletes the call and the string building before it. The release build then logged no D/BookNest lines at all and shrank by another 32,768 bytes. The everyday forms:
| Rule | Keeps |
|---|---|
| -keep class com.example.Foo { *; } | The class, its name and all members |
| -keepclassmembers class * { @Json <fields>; } | Annotated fields, if their class survives |
| -keepnames class com.example.** | Names only; unused code may still go |
| -keepclasseswithmembers class * { native <methods>; } | Classes with JNI methods |
Keep as little as possible: -keep class com.example.** { *; } "fixes" everything by switching R8 off for your code. Prefer @Keep (from androidx.annotation) on reflective entry points.