How we sped up Android and iOS builds by 68% on CI

How we sped up Android and iOS builds by 68% on CI

by Zexuan Wang , Josh Abrams , Murtaza Javaid , and Sharanya V.

We validate our code changes as part of continuous integration (CI) and have checks like tests and linters before and after a developer merges their code onto the main branch. We call them pre-merge and post-merge checks on CI.

Imagine waiting over 50 minutes for every pre- and post-merge check on your code. Sounds painful, right? Now imagine running through this grind several hundred times a week. That was our reality until we, the Test and Release Infrastructure (TRI) team, decided enough was enough.

Fast forward to today: those 50-minute waits? Gone. Android and iOS pre- or post-merge times now clock in at under 16 minutes. To iteratively get to the 16-minute mark, we measured the time to build the app on CI, a.k.a, build time, and made specific changes to bring this down. Build time is relevant because we built the app from scratch (without cached artifacts) every time there was a code change to run tests or linters. ?


What worked (a.k.a., our secret sauce)

1. Newer, shinier machines

Let’s face it: the old AWS instance types running our builds were like trying to watch high-definition videos on a dial-up connection. Upgrading them was an obvious first step.

  • For Android: We tested 16 instance types and learned the hard way that throwing more CPU power at a JVM doesn’t help—it’s a memory hog! The r7a instances, with their ample memory and reliable availability, turned out to be perfect. Result? A 21% improvement in CI build times. Oh, and pro tip: Fast disk I/O (like NVMe SSDs) speeds up Android builds even more. That’s next on our to-do list.
  • For iOS: Apple Silicon M2 Macs are much more efficient than the old x86-based mac1.metal instances. By switching to EC2 mac2-m2.metal machines, we slashed build times by a whopping 60% on CI. Developers were able to notice the improvement here immediately.And yes, we made peace with slightly higher costs. Newer machines are pricier, but quicker builds made them worth every penny.

2. More parallelization, less bottleneck drama

CI pipelines are like crowded intersections. Too much happening in one place? Total traffic jam. We revamped our pipelines to keep tasks moving smoothly:

  • For Android: We rearranged and optimized tasks, reducing serial execution time by 20% on CI. This was key to balancing our spending on those shiny new machines.
  • For iOS: We killed long startup delays by ditching Jenkins Pipelines for freestyle jobs. Startup times shrank, and our developers got their builds faster. (Bonus: we built a nifty Python library to maintain some of the syntactic sugar we loved from Pipelines!)

3. Caching is king

Our local incremental builds on both clients are under 30 seconds. We wanted to apply some of our learnings on CI to help with runs without major code changes.

  • Dependency Caching: Cached dependencies meant fewer downloads and less network latency. For iOS, we leveraged? SwiftPM’s built-in caching, shaving ~4 minutes off of CI build times.
  • Task Caching: For Android, we tapped into Gradle’s remote S3 cache. This avoided redundant compilations and reused outputs whenever possible. Result? 25% faster builds on CI.

4. Toolchain upgrades (a.k.a., spring cleaning)

Sometimes, all you need is an upgrade. We waved goodbye to KAPT (Kotlin annotation processing) and embraced KSP, which is faster and sleeker. Along with a Kotlin bump, this cut Android CI build times by another 30%.


What didn’t work (but was worth a try)

XCRemoteCache woes

We had high hopes for XCRemoteCache, a tool for iOS remote caching. But after months of head-scratching over cache misses and mysterious build failures, we had to call it quits. Lesson learned? Not every tool fits every setup. Bazel is next on our radar.


Results: the glow-up in numbers and sentiments


Key takeaways (i.e., what we learned)

  1. Small Wins Matter: Even shaving a few seconds off CI times makes a big difference over hundreds of PRs. Developers notice—and appreciate—these changes.
  2. Speed + Stability = Happy Developers: Slow, glitchy builds can derail productivity. Fixing outliers and adding retry logic kept things smooth, fast, and frustration-free.
  3. Reduce Waste, Responsibly: New machines meant higher costs, but smarter resource allocation (hello, Savings Plan and EC2 Autoscaling Groups!) kept things in check.


The bottom line

Improving build times isn’t just about better CI—it’s about creating a better developer experience. And when your developers are happy, everyone wins.?The 16-minute runs help with shipping feature changes more seamlessly, and we continue to amplify the experience through faster/automated code reviews, approvals, and merge/deployment processes on the DevX AI team.

If you want to work at a place that values and optimizes for engineering ease and excellence, we’re hiring!

Ashish C Joseph

App Dev & Support Engineer II

3 周

On which machine are you building? Build time is also dependent on the machine, isn't it?

回复
Gabriel Engel

Software Engineer | Node.JS | React | Typescript

1 个月

As a Software Engineer and a daily user of the app, I liked it! ??

回复
Lubos Lehota

Remote iOS Developer at Qinshift

1 个月

So you bought new machines for iOS. I mean good, but I was expecting something more useful. Did you try to check the build timelines for compile time optimization?

回复
Martin Masevski

Lead QA & Front End Engineer | Vue.js & Nuxt.js Specialist | Accessibility Advocate | Expert in Automated Testing

1 个月

That's fantastic!

回复
Sidney Pham-Izulu ??

Visual and Brand Design Professional

1 个月

Can't wait to see the chat feature on Duolingo, cus I keep staring at my followers with my big eyes...

回复

要查看或添加评论,请登录

Duolingo的更多文章

社区洞察

其他会员也浏览了