How do you architect and implement Dynamic Feature Modules in an Android application to reduce initial download size and deliver features on demand?
Evaluate the candidate's ability to reduce Android app size and optimise delivery using Dynamic Feature Modules, the Play Core Library, and reversed dependency injection structures. Use this MOBILE answer to show the decision, trade-off, and evidence rather than a memorised definition.
What the interviewer is scoring
- Whether they understand the structural changes required to decouple a feature into a dynamic module.
- Does the candidate effectively use the Play Core Library to manage on-demand downloads and installations?
- That they address the complexities of dependency injection and navigation across dynamic boundaries.
- Whether the candidate handles failure scenarios, such as network drops or insufficient storage during module installation.
- Whether they consider the testing and CI/CD implications of building App Bundles with dynamic modules.
Answer
Short answer
Dynamic Feature Modules reduce initial Android download size by moving heavy, low-frequency features out of the base app and delivering them on demand. The trade-off is runtime install state, offline failure handling, navigation, dependency injection, and App Bundle testing complexity.
Why micro-optimisation cannot fix a bloated APK
As flagship Android applications grow organically over the years, they inevitably accumulate complex, heavy features such as machine learning models, augmented reality filters, and extensive customer support SDKs. The APK size balloons, leading to a noticeable drop in conversion rates in emerging markets and a spike in uninstalls due to users running out of device storage. The naive approach is to mandate rigorous ProGuard rules, compress assets, and aggressively remove unused code. While necessary, these micro-optimisations merely delay the inevitable. They cannot compensate for the fundamental bloat of shipping features that only a fraction of the user base actually utilizes.
Reversing the dependency hierarchy
Drastically reducing the initial download size requires a structural paradigm shift using Android's Dynamic Feature Modules (DFM). By analyzing the application's structure with the APK Analyzer, one can typically identify that massive features—like AR and ML models—account for the majority of the binary size despite low usage metrics. Extracting these into on-demand dynamic modules is the logical architectural step.
This requires restructuring the Gradle project so that the base module contains only the core application logic, networking stack, and essential onboarding flows. Crucially, the dynamic modules must depend on the base module, explicitly reversing the traditional dependency inversion where the base application depends on its features.
Runtime state and resilience
Utilising the Play Core Library to manage the downloading and installation of these modules at runtime introduces significant state management complexity. When a user requests an on-demand feature, the application must initiate a SplitInstallRequest to the Google Play Store and carefully manage the state machine, observing the SplitInstallStateUpdatedListener to handle states such as PENDING, DOWNLOADING, INSTALLING, and INSTALLED.
Handling failure scenarios is critical. Robust error handling must be implemented for cases where network connectivity drops during the download or when the device lacks sufficient storage to unpack the module. The application must gracefully degrade, informing the user of the failure and providing actionable steps rather than crashing unexpectedly. Furthermore, configuring conditional delivery ensures that high-resolution assets are only downloaded on devices with high-density screens, further optimising the footprint.
The routing and injection dilemma
The architectural separation introduces significant challenges with dependency injection and navigation. Because the base module does not know about the dynamic modules at compile time, static routing or direct class instantiation is impossible.
A reflection-based routing mechanism or deeply decoupled interface-based navigation is required to transition between the base app and dynamic features. For dependency injection, frameworks like Dagger or Hilt must be adapted to support component dependencies across module boundaries. This ensures that the dynamic module can access singletons provided by the base module without creating memory leaks or unresolved references.
Delivery pipelines
Testing and continuous integration pipelines must be updated to generate Android App Bundles (AAB) instead of APKs, ensuring that the dynamic delivery configuration is correctly packaged. Local testing strategies using the bundletool CLI are necessary to allow developers to simulate the downloading and installation of dynamic modules during local development.
flowchart TD
A["Base Module (Core Logic & UI)"] -->|Depends On| B["Third-party SDKs"]
C["Dynamic Feature: AR Filters"] -->|Depends On| A
D["Dynamic Feature: ML Models"] -->|Depends On| A
E["Play Core API"] -->|Triggers Download| C
E -->|Triggers Download| D
A -->|Requests Installation| EMigrating to Dynamic Feature Modules requires reversing traditional dependency structures and implementing robust runtime state management. By decoupling heavy, infrequently used features and delivering them on demand, you can significantly reduce app size, improve acquisition rates, and optimise the user experience for storage-constrained devices.
© 2026 Preptima. Originally published at preptima.com.
Likely follow-ups
- How do you decide which features are worth the added complexity of extraction into a dynamic module versus keeping them in the base APK?
- What happens to the user experience if a user requests a dynamic feature while completely offline?
- How would you migrate an existing single-module app to this architecture without shipping a broken build to production?
Related questions
- How do you leverage WorkManager to guarantee execution, handle conflict resolution, and optimise battery usage in an offline-first Android application requiring bidirectional synchronisation?hardAlso on mobile and android3 min
- How do you design the dependency graph, handle shared resources, and optimise build times when breaking down a massive iOS monolithic application into a modular architecture using Tuist?hardAlso on mobile and architecture3 min
- How do you resolve severe performance bottlenecks in a complex React Native application caused by heavy bridge traffic, and how do you evaluate a migration to the JavaScript Interface (JSI)?hardAlso on mobile and architecture3 min
- What does structured concurrency actually give you in Kotlin coroutines, and how does a coroutine scope leak happen despite it?mediumAlso on kotlin and android4 min