AX notes · Measurement

Play Console said our DEX was 39% optimized. The culprit was a keep rule for an SDK we never used

A leftover Kakao SDK ProGuard file kept every public member of every class out of R8; deleting it moved the bundle's r8.json numbers from 39% to 85%.

By SugarMount Android development 7 min read Last updated

Play Console still rated the latest bundle of SugarSpeller, our Korean spelling checker, as "intermediate" on September 25, 2026: optimization 36%, obfuscation 39%, shrinking 39%. Five days earlier we had removed the -dontoptimize line that kept R8 from optimizing at all. The rest of the gap came from one seven-line ProGuard file for the Kakao SDK, which the app does not use. With it gone the same day, the unshrunk share in the bundle's r8.json fell from 61.47% to 15.18% (85% once subtracted from 100), and the AAB went from 18.4 MB to 11.6 MB.

Numbers come from SugarMount's app repository (sugarspeller): commits a597223 (2026-09-20) and e05f8e3 (2026-09-25), the changelog entries for 1.6.020 and 1.6.023, the post-release watch log, and an r8.json read again from a local release AAB while writing this note. Checked on October 20, 2026.

Contents (7)
  1. The Play Console figure is r8.json turned upside down
  2. Dropping -dontoptimize took optimization from 0% to 36%
  3. The merged configuration.txt had a rule on class *
  4. Before and after: r8.json and sizes for both changes
  5. The clue the agent explained away
  6. Still open
  7. FAQ

The Play Console figure is r8.json turned upside down

Those percentages line up with a file the Android Gradle Plugin (AGP) already puts in every R8-processed release bundle: BUNDLE-METADATA/com.android.tools/r8.json. Its stats block records the share of code that was not shrunk, obfuscated or optimized.

unzip -p app-release.aab BUNDLE-METADATA/com.android.tools/r8.json
"stats":{"noObfuscationPercentage":61.48,
         "noOptimizationPercentage":64.27,
         "noShrinkingPercentage":61.47}

Subtract each value from 100 and you get what Play shows: 61.47 became 39% shrunk, 64.27 became 36% optimized. We matched that from our own before-and-after numbers, not from a Google spec, but it held for the two Play readings we have (0% before, 36/39/39% after the first fix). That lets you read the metric locally before uploading anything.

The build used AGP 9.4.0 with R8 full mode, resource shrinking and the stock proguard-android-optimize.txt. Our repository notes say Play wants this metric addressed by February 2027; we could not re-check the wording on the Play screen for this note.

Dropping -dontoptimize took optimization from 0% to 36%

Commit a597223 on September 20 removed -dontoptimize from proguard-common.pro. It had been added on purpose, out of fear that optimization would break the Gson reflection that parses spelling results, and it pinned Play's optimization figure at 0%. The comparison baseline was the previous HEAD (1.6.019) built in the same environment.

Unoptimized code went from 100% to 64.25%, and uncompressed DEX from 16.31 MB to 14.84 MB. The AAB dropped from 29.95 MB to 18.33 MB, but most of that was a greeting-card editor and roughly 11 MB of its assets leaving in the same release. Optimization's own share sits inside the 1.5 MB of DEX.

Unobfuscated code rose from 54.16% to 61.46%. The commit put that down to "app code shrank, so the SDK keep rules weigh more" and moved on. Version 1.6.020 shipped on September 22. Three days later came the 36/39/39% reading from the top of this note.

The merged configuration.txt had a rule on class *

Every rule R8 actually applied ends up in one file, app/build/outputs/mapping/release/configuration.txt: the defaults, the app's six .pro files, and the consumer rules each library AAR brings along. The merged file from the October 9, 2026 release build is 1,369 lines, short enough to read end to end. Reading through it on September 25 turned up this block.

# proguard-kakao.pro (in the repo since the first commit, 2020-12-16)
-keep class com.kakao.** { *; }
-keepattributes Signature
-keepclassmembers class * {
  public static <fields>;
  public *;
}
-dontwarn android.support.v4.**,org.slf4j.**,com.google.android.gms.**

In the third rule, class * has no condition, so it matches every class in the app and in every library. Their public fields and methods can then not be removed, renamed or inlined. And the Kakao SDK (the SDK for KakaoTalk login and sharing) was not a dependency at all; even its Maven repository line in the root build script was commented out.

Commit e05f8e3 deleted the file and both build references to it, release and debug. It also deleted proguard-retrofit2.pro, which no build had ever referenced.

Do not ship an unconditional keep rule on class * in an Android app. Libraries that need rules already ship them as consumer rules inside their AAR.

Before and after: r8.json and sizes for both changes

Each pair of columns comes from a different day and a different commit, so compare within a pair. Even the same configuration drifts in the second decimal: 64.25% on September 20, 64.27% on September 25.

Metric1.6.019 baseline-dontoptimize removed (Sep 20)Before kakao file removal (Sep 25)After (Sep 25)
Not optimized100%64.25%64.27%15.68%
Not obfuscated54.16%61.46%61.48%15.14%
Not shrunknot recordednot recorded61.47%15.18%
As Play shows it (opt/obf/shrink)optimization 0% (Play screen)36/39/- (derived from r8.json)36/39/39% (Play screen)84/85/85 (derived from r8.json, not yet seen on Play)
DEX16.31 MB (uncompressed)14.84 MB15.6 MB (total)8.2 MB
AAB29.95 MB18.33 MB (asset removal included)18.4 MB11.6 MB
Bar chart: after the kakao file was removed, r8.json's unoptimized share fell from 64.27% to 15.68%, unobfuscated from 61.48% to 15.14% and unshrunk from 61.47% to 15.18%.
Deleting one seven-line Kakao SDK file took the unshrunk share from 61.47% to 15.18%.

Measured by DEX size, deleting one rule did about five times what removing -dontoptimize did. DEX shrank by 1.5 MB on September 20 and by 7.4 MB (47%) on September 25, and the AAB lost another 6.8 MB.

Bar chart: removing -dontoptimize took DEX from 16.31 MB to 14.84 MB; removing the kakao file took it from 15.6 MB to 8.2 MB.
By DEX saved, deleting one rule file did about five times what removing -dontoptimize did.

Before release, a release APK signed with the debug key ran on an API 34 emulator. A spelling check against Naver (Korea's largest web portal) came back with the two corrections the server reported, and the post-check quiz bubble, the quiz screen and Remote Config content injection all worked. The five JSON key strings Gson reads were still present verbatim in the release DEX. (A headless emulator returns black screenshots, so we read screen text with uiautomator dump instead.)

From September 22 to October 3 the post-release watch log recorded zero crashes for 1.6.020, for 1.6.024 (the first release without the kakao rule) and for 1.6.025, and closed the item as "no regression".

The clue the agent explained away

An AI coding agent (Claude) wrote both commits. On September 20 it declared in the commit message that the -dontoptimize line was the cause. It also recorded, accurately, that unobfuscated code had gone up by more than 7 points. It did not check which rule was holding that code; it filled the gap with a guess that the keep rules had not changed and the SDKs simply weighed more.

On the afternoon of September 25 the operator pasted a Play Console screenshot, noted that the bundle still looked unoptimized, and asked for a deeper analysis. Only then did the agent open the merged configuration, and the seven-line file from 2020 surfaced. Opening configuration.txt first instead of guessing would have saved five days.

Two lines went into the repository's agent instructions afterwards: never add a keep rule on class *, and check the merged result in configuration.txt. The checks we run to catch release-only R8 failures before shipping are in What to check before shipping an AI-built Android app.

Still open

FAQ

Does the Play Console DEX optimization warning block uploads?

It did not in September 2026: bundles at 0% and at 36% optimization both shipped normally. Our repository notes say Play expects it fixed by February 2027, so it is not something to leave for later.

Can I see the optimization percentage before uploading to Play?

Yes. Extract BUNDLE-METADATA/com.android.tools/r8.json from the release AAB and subtract the three stats values from 100. For this app, 61.47 matched Play's 39%. That mapping is our inference from repository records, and nothing yet records whether Play showed the 85% that 15.18 implies.

How do I find the keep rule that is holding the metric down?

Search the merged configuration.txt for -keep or -keepclassmembers rules on wide patterns such as class * or ** with no condition attached. Here it was a single -keepclassmembers class * { public *; } block in a file for an SDK the app did not use.

This note is written from values SugarMount measured itself; an AI coding agent was used to draft it.

All notes on Android development · All AX notes