Release build ClassNotFoundException not in debug: cause and file?

Tests R8 shrinking awareness: release-only obfuscation removes classes loaded by reflection. Check proguard-rules.pro first and add a -keep rule. Red flag: blaming multidex or disabling minification rather than writing a targeted keep rule.
WHAT THIS TESTS: This question tests whether you understand the difference between debug and release Android builds regarding code shrinking. Specifically, it checks if you know that R8 runs by default in release builds to shrink, obfuscate, and optimize code, and that this process can break dynamic class loading mechanisms like reflection, JNI, or serialization where class names are referenced as strings. It also checks if you know the standard location for keep rules.
A GOOD ANSWER COVERS: First, state that R8 code shrinking and obfuscation is enabled in release but typically disabled in debug. Second, explain that ClassNotFoundException in release almost always means the class was removed as dead code or renamed by obfuscation because R8 cannot trace the reference, usually due to reflection, ServiceLoader, Gson, or JNI usage. Third, name proguard-rules.pro as the first file to investigate, though you might also mention consumer-rules.pro if the issue is in a library module. Fourth, describe the fix: adding a targeted -keep rule to preserve the class name and members, such as -keep class com.example.MyClass, and then rebuilding. Optionally mention using -keepnames if you only need to prevent renaming but not removal.
COMMON WRONG ANSWERS: Blaming multidex is a red flag because multidex issues manifest differently and are unrelated to code shrinking. Suggesting you disable minification or shrinkResources in build.gradle to fix the symptom rather than the cause shows a lack of understanding of release optimization. Confusing ProGuard with R8 is minor but outdated; saying you would look at build.gradle first instead of the rules file misses the point. Another red flag is suggesting you deobfuscate the stack trace before fixing the crash; while mapping.txt is useful for reading logs, it does not fix the underlying missing class.
LIKELY FOLLOW-UPS: An interviewer might ask how you would identify the missing class if the stack trace is obfuscated, in which case you would use the mapping.txt file from the build outputs. They might ask for the difference between -keep, -keepnames, and -keepclassmembers, so you should know that -keep preserves the entire class from shrinking and obfuscation, -keepnames only prevents renaming if the class is kept, and -keepclassmembers preserves members but not the class name. They might also ask how library modules expose their own rules, which is via consumerProguardFiles pointing to consumer-rules.pro.
ONE CONCRETE EXAMPLE: Suppose you use Gson to deserialize JSON into a data class com.example.model.User that has no direct constructor calls in your Kotlin code. In debug, the class exists and Gson finds it via reflection. In release, R8 sees no static references to User and removes it or renames it to something like a.b.c. The app crashes with ClassNotFoundException. You open proguard-rules.pro and add a -keep rule for com.example.model.User, or better, for the entire com.example.model package, then rebuild the release APK.
Read the original → developer.android.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.