AI Briefing
KO

Solving a 200MB Module at the Team Level: Karrot's Short-form Team Adopts On-demand Dynamic Feature Module

·2026.02.27 19:12

Key point

Karrot's short-form team split off **40MB** of editing features into an **On-demand DFM**, reducing the burden on global users.

Details

As video editing features were added to Karrot Stories, the short-form module grew to 200MB, and even after moving resources to a CDN, 40MB remained. Since editing was a feature used by only some users, the team decided to change the structure where a domestic-only experiment was affecting the install size for all global users.

The solution chosen was Dynamic Feature Module (DFM). Instead of bundling it in at install time, they switched to On-demand Delivery, which downloads the module only when a user taps the edit button, so non-users don't receive the 40MB at all.

During implementation, several practical constraints emerged.

  • Hilt/DI constraints: Since DFM is built separately from base, Hilt cannot know about DFM's @Inject and @Module at base build time. In the end, base needed to expose dependencies via EntryPoint, while DFM needed to configure its own Dagger Component directly.
  • SplitCompat configuration: To view the code and resources of a split APK installed on-demand, SplitCompat was required. Without this configuration, ClassNotFoundException or resource loading failures could occur.
  • SO file management: System.loadLibrary() could not find native libraries located in the DFM, causing UnsatisfiedLinkError, so SplitInstallHelper.loadLibrary() had to be used instead. They also had to align ndk.abiFilters between base and DFM, and watch out for STL conflicts such as libc++_shared.so.
  • R8 rule management: Keep rules placed only in the DFM caused base types to get obfuscated, so the team resolved this by consolidating keep rules in base.

After much trial and error, the structure became simpler. It converged on keeping the feature code in the base module and splitting off only the SO files, which accounted for most of the size, into the DFM. This was because the code itself was only a few hundred KB, while the actual target for size reduction was the native libraries.

The advantages of this approach were clear.

  • Even if DFM loading fails, the entire app doesn't crash — it can simply notify the user that the SO file is missing.
  • Local development and debugging become much easier.
  • Splitting off just the SO files alone achieved over 90% size reduction.

On the other hand, the structure of separated code and SO files could be unfamiliar to engineers seeing it for the first time, and the flow from checking installation status to SO loading to feature execution had to be explicitly managed. Still, the core goal was achieved: a structure that runs a domestic-only short-form editing feature stably without burdening global users.

In the end, three operational points were identified. Stability meant not affecting other teams or the global service; independence meant the team could develop and experiment autonomously; and operational efficiency meant anyone could understand and fix it. Ultimately, this project concluded that "running it stably" mattered more than "making it look cool."

The appendix also presented testing methods for applying DFM. In a real environment, the download flow is verified using Play Console internal testing or Internal App Sharing, while locally, bundletool is used to alternately generate base-only, DFM-included, and universal APK states for verification. Before applying it, module size, download timing, SplitCompat placement, and both installed/uninstalled cases should all be checked, and during operation, download success rate, SO loading failure rate, edit entry success rate, and network retry success rate should be monitored.

This summary was generated automatically by AI. Check the original for the author's claims and context. Copyright belongs to the original author.

Our guide explains how the AI works. Report summary errors, attribution issues, or removal requests via Contact.