Keeping IDE Sync Times at Bay: A Historical Perspective
Commemorating 10 years since Android Studio's introduction
Ten years ago at Google's I/O 2013 conference, engineering director Tor Norbye unveiled Android Studio, a state-of-art integrated development environment (IDE) for building, testing, and shipping apps. Demonstrating the capabilities of this new tool, Tor showed how it would make developers more productive and efficient. Android Studio is based on JetBrains' popular IntelliJ platform, which allowed Google to launch a fully-featured editor out of the starting gate.
A key decision as part of the 1.0 launch was to integrate with the Gradle build automation tool, an open-source project initially released by Hans Dockter and Adam Murdoch in 2008. The goal of this integration was to provide a way for building not only from the command line but also in continuous integration (CI) environments and within the IDE. For this to work, the Gradle build system and the IDE needed to present a consistent experience, so any changes in Gradle would reflect correctly in Android Studio. To accomplish this feat, a synchronization process, also known as "sync", ensured that the build system and the IDE worked in harmony.
For Square's large codebase, as the number of Gradle subprojects grew from 1800 to 4400 over the past two years, the IDE sync times emerged as one of the top complaints from our Android developers. This sync process and subsequent indexing time, which would take as long as 20-25 minutes, was necessary for the IDE to navigate magically through the code base, provide autocomplete suggestions, or simply surface meaningful context hints or suggestions. Without it, none of these important features could be used in Android Studio.
One of the challenges was trying to isolate the problem. Was it related to the fact that Android Studio often lagged the IntelliJ releases by at least a year, and perhaps the issue had already been fixed? Was it related to one of various IntellIJ plugins? Or did the problem live in the Gradle build system? Pinpointing where these types of issues lived has been one of the biggest challenges, akin to site reliability engineers trying to ascertain a bottleneck by observing the interactions between different systems.
Because performance regressions in one part of the system created huge swings in the IDE experience, we tried to better understand these issues. For instance, Square engineer Stéphane Nicolas discovered an inefficiency in the IntelliJ Kotlin plugin resolving its dependencies that led to reducing sync time by 3 minutes for Android Studio v4.1. Trying to diagnose why freezing was becoming a common issue, Pablo Baxter figured out how we could attach breakpoints to a running process of Android Studio against the latest source code of Google's Android IntelliJ plugin. As we began to understand that the sync times were negatively impacted by our modularization strategy, primarily driven by the Gradle subproject growth in our large codebase, Tony Robalik sped up traversing our build graph by 400% based on the app being built in our monorepo.
We also experimented with other approaches that didn't quite pan out. For instance, when experimenting with cloud IDEs, we found that the sync time didn't improve regardless of how much CPU and memory we gave Android Studio. Our foray into using the Bazel, an open source version of Google's internal build system, was based in part on an assumption that the IDE experience would be better, especially since the Bazel IntelliJ plugin offered a partial sync option. Ultimately, the Android Studio issues became a higher priority and our attempted conversions to Bazel exposed a lot of technical debt in our build logic that led us to work on herding and stampeding elephants, a reference to Gradle's company logo.
Fundamentally, the IDE sync issue needed to be solved with the parallel import feature, which landed in the January Android Studio Electric Eel release. It took two years for it to happen because of the various changes that needed to be made and tested at scale. First, new data models needed to be developed by Google, which began to land in Android Studio v4.2. Second, Gradle needed to add support for parallelization in their Tooling API, which provides an abstraction layer for evolving versions. Finally, the data from Gradle needed to be translated to IntelliJ models in parallel, primarily the projects, facets, and library information for use in the IDE.
There is still room to improve, especially parts of the models during the sync that are still exchanged sequentially. JetBrains, Google, and Gradle have also begun leveraging a sanitized representation of our project structure generated by Google's open source replicator tool, which should hopefully enable faster iterations to validate improvements. In addition, Gradle is continuing to focus on Project Isolation, which should help further speed up the IDE sync performance.
Regardless, the parallel import feature has been a game changer for us. It has resulted in a 60% speed improvement and saved an estimated 1600 developer hours per year. While there is still room to improve, the new feature has already been a stark contrast in the IDE experience for Android Studio. Grant Park, an Android developer who struggled with many of these issues, summed it up best: "I can't believe my eyes… already [it] seems like every problem went magically away. If this keeps up, I think my life here as a developer has just improved 100x."
I still remember when version 0.2 of Android Studio first was released ten years ago. In this early preview release, the integration between Gradle and Android Studio had yet to be added, so adding a dependency to Gradle still required making changes through the IDE's user interface. That extra step is no longer needed, and we've come full circle in making the sync process faster and more performant for large codebases such as ours. Thank you to our industry partners for their support in improving Android Studio over this past decade!
(Special thanks to Dan Taylor, Korhan Bircan, Piotr Jagielski, Tim Mellor, and Josh Friend for reviewing drafts of this post.)