Ishan Khanna Profile picture
👨‍💻 Mobile Engineering @TinderEng 🔥 @GoogleDevExpert for Android ✍️ Writer @ https://t.co/6CC7hJyDss 🌏 International Tech Speaker

Nov 17, 2022, 15 tweets

This is a great question to refresh and revise what I have learned over the years from my ex-colleagues especially @inyaki_mwc & @jperezalv
I'll try to summarize what usual suspects I can recall in the 🧵

Usually, the answer lies in your build scans which is an amazing feature of the #Gradle build tool.
A Gradle build scan tells you how much time was spent in each of the three phases of the build lifecycle namely:

👉 Initialization
👉 Configuration
👉 Execution

Before going further let's refresh a few points
👉 Gradle supports single and multi-project builds.
👉 In Gradle tasks are the building block of your build process
👉 Tasks can depend on each other
👉 Gradle guarantees that tasks are executed in the order of their dependencies

How is the order determined? 🤔
Through a "Directed Acyclic Graph"

⚠️ An important point to keep in mind is that Gradle creates the dependency graph prior to executing any single task.

Speaking in terms of a Tree, you want to keep the height of your modules as small as possible

Suspect 1️⃣ - You have a module with a "high" level of dependencies and something changed "close" to the bottom of your tree.

⚠️ Dependency depth matters more than the number of dependencies at the same level.

💡 Keep your module dependency depth as low as possible.

Suspect 2️⃣ - Annotation Processors (AP)

Whenever looking into build problems my first question is what all annotation processors are we using?

⚠️ APs add additional tasks to your build execution phase.

Common libs that use it -
Dagger, Glide, Moshi, Room, etc.

Suspect 3️⃣ - Build Cache Disabled
🤑 Gradle supports build caching.

How? 🤔
Each "task" takes an Input and spits an Output.

When Gradle can deterministically say that an Input of the task didn't change, it can skip executing the task and reuse the output from the cache.

Use ⤵️

Suspect 4️⃣ No Parallelism
Assuming most people have machines that have more than one processing core, not leveraging the ability of Gradle to execute certain tasks in parallel can also slow down your builds.

Use ⤵️

Suspect 5️⃣ Gradle Daemon Memory Starvation

Gradle builds use a JVM that requires memory and if doesn't have enough, it will have to constantly swap out items which result in cache misses, and slower builds.

Try altering gradle.properties to set heap size ⤵️

Suspect 7️⃣ Inefficient Garbage Collection

Try altering gradle.properties to use Parallel GC ⤵️

Suspect 8️⃣ Running Irrelevant Tasks
A lot of times devs are running tasks with certain plugins or due to a setup that they actually don't need/care about.
🙅‍♂️Firebase perf plugin on debug builds
🙅‍♂️Modifying build params that lead to task cache invalidation
🙅‍♂️Png Crunching etc.

Suspect 9️⃣ Dynamic dependency resolution

Ever seen dependencies versions like `1.3+`
This allows gradle to dynamically resolve dependencies but they have side effects
👉 Gradle needs to resolve them (rather than just fetch/download them)
👉 Upstream Updates can break your app

Suspect 🔟 Bad Repository Ordering
If you use multiple repositories in your project. Gradle will look for your deps in that order. So the repository that contains high number of deps for your project should be listed first, there by saving unnecessary network requests.

That's it for today, I'll probably compile this into a blog later with more items that contribute to Gradle build times (in a way you don't want)

If you found this helpful, don't forget to RT this first tweet ⤵️

Share this Scrolly Tale with your friends.

A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.

Keep scrolling