Teams should inventory every runtime lookup path, then preserve only the classes and members those paths truly need. The safest approach is to test release-like builds early, capture failures through profiling or stack traces, and narrow keep rules until runtime behaviour remains stable without overexposing code.
Why reflection-heavy Android code breaks under shrinking
Code shrinking is most brittle where an app discovers classes, methods, or fields at runtime instead of through normal compile-time references. Reflection, generated names, dynamic proxies, and some serialization patterns can all hide real dependencies from the shrinker, so a build that compiles cleanly may still fail only after minification, obfuscation, or optimization.
The practical issue is not that shrinking is unsafe, but that the shrinker can only preserve what it can see or what you explicitly tell it to keep. If runtime lookup paths are not mapped accurately, the app may crash, silently skip behaviour, or lose a feature only in release builds.
A good way to think about this is that every reflective entry point becomes part of the app’s compatibility surface. Teams that document and test those paths early usually avoid the worst surprises, and they can apply tighter rules with more confidence. For broader mobile testing patterns, the Mobile App Security Testing Guide is useful because runtime analysis and release-like testing surface the same kind of hidden behaviour that shrinking can expose.
How to preserve only what runtime really needs
Start by inventorying every place the app resolves code dynamically, including reflection, annotation-driven frameworks, JSON binding, ORM adapters, plugin loading, and any library that instantiates types by name. Then map each lookup to the exact classes, methods, constructors, and fields it requires, rather than preserving whole packages or broad namespaces by default.
Keep rules should be as narrow as possible while still being readable and maintainable. Overbroad rules defeat the point of shrinking, increase attack surface for reverse engineering, and make it harder to tell whether a failure came from a real dependency or from an unnecessary preserve rule. In mobile codebases with deep framework usage, the Android App Security Guide gives useful context on how build-time and runtime hardening choices interact.
When a library relies on naming conventions, keep the names that runtime resolves, not every member of the containing class. If the app only needs constructors or specific annotated methods, preserve those rather than disabling shrinking for the entire component. That approach keeps release artifacts smaller and makes it easier to detect when a new reflective dependency has been introduced.
How to validate shrinker rules before release
Validation should happen against release-like builds, not only debug builds, because debug artefacts often mask the exact failure mode. Run the app through the flows that touch reflective code, capture stack traces, and confirm that the shrinker has not removed any class or member that runtime resolution depends on.
Instrumented tests and runtime logs are most valuable when they prove both correctness and restraint, meaning the app still works and the keep rules are no broader than necessary. If a failure appears only after obfuscation, treat it as a mapping problem first, not as a reason to disable shrinking globally. For teams that also need guidance on the surrounding mobile attack surface, the Mobile App Security Guide is a useful companion reference.
Where a library or framework is known to use reflection heavily, test at the integration boundary as well as in unit tests. That is the point where a keep rule mistake usually becomes visible, because the app is exercising the exact runtime lookup path the shrinker cannot infer from source alone.
Risk and Threat Considerations
Reflection-heavy apps are more likely to ship brittle release builds because the shrinker cannot infer every dynamic access path. The main operational risk is a delayed failure that appears only after minification, while the main security risk is over-preserving code when teams respond by broadening keep rules instead of narrowing them carefully.
Failure mechanism: Dynamic lookup hides the true dependency graph, so the shrinker removes a class, constructor, or method that runtime code still expects to find by name or annotation.
Impact: The app may crash, skip security-critical behaviour, or leave unnecessary code reachable in the shipped binary, which weakens both reliability and code-exposure hygiene.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
OWASP ASVS, OWASP SAMM and CIS Controls v8 set the governance and control requirements practitioners need to meet.
| Framework | Control / Reference | Relevance |
|---|---|---|
| OWASP ASVS | V15 — Secure Coding and Architecture | Reflection-heavy Android code needs architecture-aware handling of dynamic dependencies. |
| Recommendation — Document dynamic lookup paths and keep shrinker rules as narrow as possible. | ||
| OWASP SAMM | Architecture Definition — Architecture Definition | Build-time shrinker behaviour depends on understanding hidden runtime dependencies in design. |
| Recommendation — Capture reflection and generated-code dependencies during architecture design. | ||
| CIS Controls v8 | CIS-16 — Application Software Security | Shrinking misconfigurations can break shipped app behaviour and require secure release testing. |
| Recommendation — Test release-like builds with minification enabled before deployment. | ||
Practitioner Guidance
What to prioritise: Treat every reflective path as a release-blocking dependency until it has been exercised in a shrunk build. The goal is not to keep shrinking simple, but to prove that the smallest possible preserve set still supports every runtime lookup path.
What to verify: Confirm that each keep rule maps to a concrete runtime need, not to a library’s entire package or a past failure that is no longer relevant. If a rule cannot be tied to a specific lookup or member access, it is usually too broad.
Practitioner takeaway: The safest shrinker strategy is evidence-driven preservation, not defensive overkeeping, because the right rule set is the one that survives real release execution without exposing more code than runtime actually needs.
Related resources from NHI Mgmt Group
Deepen Your Knowledge
Free weekly newsletter
Subscribe to the NHI & AI Identity Journal
The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.
Bonus 33% off our NHI Course when you subscribe.
Reviewed and updated by the NHIMG editorial team on November 4, 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org