$30 off During Our Annual Pro Sale. View Details »

Improve the build times of your project is not impossible. How we achieved it in N26.

antonicg
September 26, 2023

Improve the build times of your project is not impossible. How we achieved it in N26.

In 2021/2022 N26 android platform team focused on reducing the build times of the project. We achieved to reduce them a 75%! This talk is about how we achieve that successful result that made the N26 Android Engineers happy and more productive.

I will present the implementations that N26 platform team did to reduce the compilation time of our app module from 20-25minuts to 5-6minuts. Also we reduced the compilation time of the library modules to improve the productivity of the engineers. I will talk about the gradle dependency graph, DI (Anvil plugin from square) and about the Dependency Inversion Principle.

antonicg

September 26, 2023
Tweet

Other Decks in Programming

Transcript

  1. Antoni Castejón
    Improve the build times of your
    project is not impossible.
    How we achieved it in N26

    View Slide

  2. Why?

    View Slide

  3. STAKEHOLDERS
    👨💻👩💻👨💻👩💻👨💻👩💻👨💻👩💻

    👨💻👩💻👨💻👩💻👨💻👩💻👨💻👩💻

    👨💻👩💻👨💻👩💻👨💻👩💻👨💻👩💻

    👨💻👩💻👨💻👩💻👨💻👩💻👨💻👩💻
    Android Team
    (Product)
    Mobile Core
    👨💻👩💻

    👨💻👩💻

    View Slide

  4. 😭 ☹ 😶 🙂 🤩

    View Slide

  5. View Slide

  6. It was highly demanded by our
    stakeholders

    View Slide

  7. But… It was that painful to work
    with the project?

    View Slide

  8. 24 minutes
    Full app build time (./gradlew clean app:assembleDebug)
    🥲

    View Slide

  9. The N26 android project

    View Slide

  10. core_lib core_lib core_lib
    core_lib
    core_lib core_lib core_lib
    core_lib
    core_lib core_lib core_lib
    core_lib
    core_lib core_lib core_lib
    core_lib
    single_feature_app 1
    feature_app
    feature_lib
    single_feature_app 2
    feature_app
    feature_lib
    single_feature_app 3
    feature_app
    feature_lib
    single_feature_app 4
    feature_app
    feature_lib
    The N26 android project
    Setup before the initiative
    app core libraries folder
    features folder

    View Slide

  11. single_feature_app 1
    feature_app
    feature_lib
    The N26 android project
    Single feature apps
    • The feature code

    • Runs independently

    • Entry point

    • Less dependencies

    • Less build time

    View Slide

  12. feature_app
    The N26 android project
    Single feature apps
    • Login

    • Activity entry point

    • No production code

    • com.android.application

    View Slide

  13. feature_lib
    The N26 android project
    Single feature apps
    • Feature code

    • Production code

    • com.android.library

    View Slide

  14. The N26 android project
    Overview before the initiative
    • 312 total modules

    • 119 core libraries

    • 70 SFAs

    View Slide

  15. How?

    View Slide

  16. View Slide

  17. 🙅

    View Slide

  18. 🤔

    View Slide

  19. Then, what were the issues of the
    project impacting the build time?
    🤔

    View Slide

  20. • The Gradle dependency graph

    • Legacy monoliths

    View Slide

  21. The Gradle dependency graph

    View Slide

  22. Chat GPT 3.5
    A visual representation of the dependencies between
    different components in a Gradle-based project. It refers to
    the relationships between different modules, libraries, and
    other dependencies that make up a gradle application.
    The Gradle dependency graph

    View Slide

  23. https://github.com/jraska/modules-graph-assert

    View Slide

  24. generateModulesGraphStatistics

    View Slide

  25. Wikipedia - Glossary of graph theory (https://en.wikipedia.org/wiki/Glossary_of_graph_theory#height)
    The height of a node in a rooted tree is the number
    of edges in a longest path, going away from the
    root, that starts at that node and ends at a leaf.

    View Slide

  26. Number of nodes, counting vertically, in
    the path from the root to the last one.

    View Slide

  27. View Slide

  28. 20
    The height of the app module

    View Slide

  29. Hypothesis

    View Slide

  30. Reducing the height of the graph
    the build time will be reduced

    View Slide

  31. To flat the dependency graph

    View Slide

  32. View Slide

  33. Height = 5

    View Slide

  34. View Slide

  35. Height = 2

    View Slide

  36. To reduce the height

    View Slide

  37. Why?

    View Slide

  38. Because of how Gradle build
    works

    View Slide

  39. About Gradle
    Parallel execution

    View Slide

  40. About Gradle
    Parallel execution
    🔄
    🔄
    🔄
    🔄 🔄
    🔄 🔄 🔄 🔄 🔄
    🔄 🔄
    🔄

    View Slide

  41. About Gradle
    Parallel execution
    🔄
    🔄
    🔄
    🔄
    🔄
    🔄 🔄 🔄

    View Slide

  42. About Gradle
    Parallel execution
    🔄
    🔄
    🔄
    🔄

    View Slide

  43. About Gradle
    Parallel execution
    🔄
    🔄
    🔄

    View Slide

  44. About Gradle
    Parallel execution
    🔄

    View Slide

  45. About Gradle
    Parallel execution
    🔄

    View Slide

  46. About Gradle
    Parallel execution

    View Slide

  47. About Gradle
    Executing a gradle task…
    Current execution
    Previous execution
    Skips execution!
    Gradle incremental builds and caching
    Outputs
    Outputs

    View Slide

  48. About Gradle
    Gradle incremental builds and caching
    Up-to-date checking

    View Slide

  49. About Gradle
    But…
    Current execution
    Previous execution
    Reexecution is needed
    for those tasks
    Gradle incremental builds and caching
    Reexecution is NOT only
    needed for those tasks…

    View Slide

  50. About Gradle
    Gradle incremental builds and caching
    Reexecution is NOT only
    needed for those tasks…
    … but also for the
    tasks that depend on
    them

    View Slide

  51. About Gradle
    Gradle incremental builds and caching

    View Slide

  52. About Gradle
    Gradle incremental builds and caching

    View Slide

  53. About Gradle
    Gradle incremental builds and caching
    🔄
    🔄
    🔄
    🔄
    🔄

    View Slide

  54. About Gradle
    Gradle incremental builds and caching

    View Slide

  55. About Gradle
    Gradle incremental builds and caching

    View Slide

  56. About Gradle
    Gradle incremental builds and caching
    🔄
    🔄
    🔄

    View Slide

  57. Solution

    View Slide

  58. Apply dependency inversion
    Solution proposed for the hypothesis
    • High-level modules should depend on abstractions rather than in low-level
    modules.

    • Abstractions should not depend on details. Details should depend on
    abstractions.
    SOLID
    D

    View Slide

  59. Apply dependency inversion
    Solution proposed for the hypothesis
    public interface NetworkApi {
    fun connectToInternet()
    fun disconnectFromInternet()
    }
    Abstraction
    public class Network : NetworkApi {
    override fun connectToInternet() {
    // Starting connection...
    }
    override fun disconnectFromInternet() {
    // Disconnecting...
    }
    }
    Details

    View Slide

  60. Apply dependency inversion
    Solution proposed for the hypothesis
    High-level modules should depend on abstractions rather than low-
    level modules.
    feature_lib
    details
    feature_lib
    abstraction

    View Slide

  61. Apply dependency inversion
    Solution proposed for the hypothesis
    Abstractions should not depend on details. Details should depend
    on abstractions.
    feature_lib
    abstraction
    details
    feature_lib
    abstraction
    details

    View Slide

  62. Apply dependency inversion
    Solution proposed for the hypothesis
    Abstraction and details module for
    every potential dependency

    View Slide

  63. public class Network : NetworkApi {
    override fun connectToInternet() {
    // Starting connection...
    }
    override fun disconnectFromInternet() {
    // Disconnecting...
    }
    }
    public interface NetworkApi {
    fun connectToInternet()
    fun disconnectFromInternet()
    }
    Apply dependency inversion
    Solution proposed for the hypothesis
    abstraction details
    api core

    View Slide

  64. Apply dependency inversion
    Solution proposed for the hypothesis
    core_lib core_lib core_lib
    core_lib
    core_lib core_lib core_lib
    core_lib
    core_lib core_lib core_lib
    core_lib
    core_lib core_lib core_lib
    core_lib
    single_feature_app 1
    feature_app
    feature_lib
    single_feature_app 2
    feature_app
    feature_lib
    single_feature_app 3
    feature_app
    feature_lib
    single_feature_app 4
    feature_app
    feature_lib
    app core libraries folder
    features folder
    core_lib

    View Slide

  65. Apply dependency inversion
    Solution proposed for the hypothesis
    core_lib
    api
    core

    View Slide

  66. Before dependency inversion
    app
    feature_app
    feature_lib
    core_lib core_lib
    core_lib

    View Slide

  67. feature_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    Before dependency inversion
    app
    feature_app
    feature_lib
    core_lib lib
    core_lib
    core_lib
    😭

    View Slide

  68. feature_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    Before dependency inversion
    core_lib

    View Slide

  69. feature_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    Before dependency inversion
    core_lib
    🔄
    🔄
    🔄
    🔄
    🔄
    🔄
    SEQUENTIALLY

    View Slide

  70. With dependency inversion
    app
    feature_app
    feature_lib
    core_lib lib
    core_lib
    api
    api
    api api
    legacy
    app
    feature_app
    feature_lib
    core
    core
    😃

    View Slide

  71. With dependency inversion
    app
    feature_app
    feature_lib
    core_lib lib
    core_lib
    api
    api
    api api
    legacy
    app
    feature_app
    feature_lib
    core
    core
    🔄

    View Slide

  72. Proof of concept (POC)
    feature_
    fl
    ag
    api
    core

    View Slide

  73. 35%
    Build time reduced for a library module (from ~28 to ~18 sec)

    View Slide

  74. 76%
    Edges count reduced (from 69 to 17)

    View Slide

  75. 56%
    Height reduced (from 9 to 4)

    View Slide

  76. View Slide

  77. View Slide

  78. What about a feature library?

    View Slide

  79. 11%
    build time reduction for our feed library module

    View Slide

  80. 1%
    for the app module

    View Slide

  81. Process we follow
    Iterative
    Discovery
    Implementation
    Benchmark

    View Slide

  82. Process we follow
    Iterative
    Discovery
    Pick the top 5 core
    libraries with:
    ✅ More edges
    ✅ Easy to apply
    Implementation
    Apply DI
    Benchmark
    Compare results:
    master changes

    View Slide

  83. Legacy monoliths

    View Slide

  84. Chat GPT 3.5
    Monolith: A monolith refers to an application that is built as
    a single, unified unit. All components and features of the
    application are tightly integrated into a single codebase,
    often resulting in a large and complex code structure.
    Legacy monoliths

    View Slide

  85. Large single module that does many
    different things
    Legacy monoliths

    View Slide

  86. Chat GPT 3.5
    Legacy: Refers to older software systems that are still in use but are no longer
    actively developed or maintained. These systems are usually built using older
    technologies, frameworks, and practices that might not align with modern
    software development trends and best practices. Legacy systems can pose
    challenges in terms of scalability, maintenance, and integration with newer
    technologies.
    Legacy monoliths

    View Slide

  87. Old code
    Legacy monoliths

    View Slide

  88. View Slide

  89. What was injectLegacy?

    View Slide

  90. Anvil

    View Slide

  91. https://github.com/square/anvil

    View Slide

  92. Then, why not using Hilt?

    View Slide

  93. Anvil VS Hilt
    Anvil
    • Kotlin compiler plugin

    • Needs Dagger

    • Merges components and
    modules using annotations

    • Doesn’t need Kapt
    Hilt
    • Dependency injection framework

    • Independent library

    • Simpli
    fi
    es Dagger DI for Android

    • Needs Kapt
    *

    View Slide

  94. Anvil VS Hilt
    Anvil
    • Kotlin compiler plugin

    • Needs Dagger

    • Merges components and
    modules using annotations

    • Doesn’t need Kapt *
    ✅ Without @Component

    ✅ Pure Kotlin

    View Slide

  95. How was Anvil applied in our
    project?

    View Slide

  96. app feature_app
    feature_lib
    feature_lib core
    With Kapt
    No Kapt

    View Slide

  97. Then, what was injectLegacy?

    View Slide

  98. core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    injectLegacy
    injectLegacy
    BaseComponent
    BaseSessionComponent
    feature_lib
    core_lib
    Legacy monoliths
    app

    View Slide

  99. What we did?

    View Slide

  100. app
    Legacy monoliths
    injectLegacy
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    injectLegacy
    BaseComponent
    BaseSessionComponent
    feature_lib
    core_lib
    ✅ Removed injectLegacy dependency
    ✅ Add Anvil plugin (if necessary)
    Done by Platform team
    1
    ✅ Adds Anvil Plugin
    ✅ Removed injectLegacy
    dependency
    Done by Product teams
    2

    View Slide

  101. Involving product teams
    1. Step by step guide

    2. Tracked the process

    3. Capacity of the teams

    4. Explained the bene
    fi
    ts
    Explained the bene
    fi
    ts

    View Slide

  102. Involving product teams
    Explained the bene
    fi
    ts
    ✅ Height of the SFA
    ✅ Skip Kapt
    The build time of their SFA
    library will be reduced
    :

    View Slide

  103. Involving product teams
    Explained the bene
    fi
    ts
    ✅ Height of the SFA
    ✅ Skip Kapt
    The build time of the app
    will be ALSO reduced
    :

    View Slide

  104. 😭 ☹ 😶 🙂 🤩

    View Slide

  105. View Slide

  106. core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    core_lib
    injectLegacy
    BaseComponent
    BaseSessionComponent
    feature_lib
    core_lib
    Legacy monoliths
    injectLegacy
    ✅ Removed injectLegacy dependency
    ✅ Add Anvil plugin (if necessary)
    Done by Platform team
    1
    ✅ Adds Anvil Plugin
    ✅ Removed injectLegacy
    dependency
    Done by Product teams
    2
    ✅ Delete injectLegacy
    Done by Platform team
    3

    View Slide

  107. Legacy monoliths
    injectLegacy

    View Slide

  108. Legacy monoliths
    injectLegacy
    🔪💀

    View Slide

  109. Results

    View Slide

  110. 20
    The height of the app module

    View Slide

  111. 11
    The height of the app module
    20

    View Slide

  112. 45% 🎉
    The height of the app module

    View Slide

  113. 24 minutes
    Full app build time (./gradlew clean app:assembleDebug)

    View Slide

  114. 6 minutes
    Full app build time (./gradlew clean app:assembleDebug)
    24 minutes

    View Slide

  115. 75% 🎉
    Full app build time (./gradlew clean app:assembleDebug)

    View Slide

  116. Take-aways

    View Slide

  117. 👀 Gradle dependency graph

    View Slide

  118. 🔁 Iterate

    View Slide

  119. 🕵 Discovery

    View Slide

  120. 📈 Continuous monitoring

    View Slide

  121. 📚 Documentation

    View Slide

  122. 💪 Challenge the status quo

    View Slide

  123. Thanks ❤
    Antoni Castejón
    @antoni_castejon

    View Slide