IntelliJ IDEA
IntelliJ IDEA – the Leading Java and Kotlin IDE, by JetBrains
Faster Time-to-Code in IntelliJ IDEA
IntelliJ IDEA’s power and wide range of functionality can make it somewhat resource-hungry. Depending on the project you’re working with, the IDE has been known to lag, which can naturally be frustrating.
Perhaps the most common scenario in which developers are required to wait is when opening a project. IntelliJ IDEA needs to load and synchronize the project, perform indexing, and do a lot of other small things to enable all the useful features it has.
In this post, we’ll look at the steps we’ve taken to improve performance in newer versions of IntelliJ IDEA, reducing time-to-code and making the IDE more responsive right from startup.
Our mission: improve time-to-code
If you take a look at an IntelliJ IDEA version from before 2023.2, the IDE had to wait for the full sync of your project’s Maven or Gradle project model before it could even start indexing. Then, while waiting for indexing to finish, all the smart features of the IDE, including code highlighting and navigation, were disabled. Only after indexing was complete could they be used.
Here’s a schematic representation of components involved:
The time it took to synchronize and index projects increased the larger the projects got. While this was to be expected, as huge projects are more complex for the IDE to work with, waiting several minutes before being able to start working was still tedious.
Unfortunately, with so many steps to perform while loading a project, significant time could be spent even on smaller ones, resulting in the perception that IntelliJ IDEA was performing slowly.
Nobody likes having to wait, especially when you’re thinking about the work you want to dive into. That’s why we made improving this situation a high-priority task.
To gauge our progress, we decided to track what we call time-to-code – the time between the application starting or the project opening and the moment when it becomes possible to comfortably work with code in it. Our goal was simply to reduce time-to-code as much as possible.
Improving the project opening flow and experience
The problem of improving startup and project opening in IntelliJ IDEA is actually complex, as it depends on several components and their interconnection. This complexity has an advantage, however, as it allows us to approach the problem from multiple angles. While we wait for longer-term efforts on technical improvements to bear fruit, we’ve also been able to adjust IntelliJ IDEA’s approach to indexing for significant time-to-code benefits that can already be experienced.
Technical improvements
One obvious approach to reducing time-to-code is performing technical updates to improve the IDE’s performance – optimizing code and architecture, using better hardware, parallelizing, etc. IntelliJ IDEA is more than 20 years old, and some architectural and algorithmic decisions made in the early days are still present in the product.
Work is being done here. We’ve invested a lot of effort into proper monitoring, investigating, and optimizing performance bottlenecks, and this has already delivered some noticeable results. We’ve improved the application’s startup Application Performance Index (Apdex) to the top category with a score of 0.94, improved the speed of multi-threaded indexing by 25%, and got rid of unnecessary locks during parallel indexing, reducing the time other threads have to wait for such locks to become available. But this is going to be a long journey, as refactorings can have consequences for other subsystems in the IDE and need to be evaluated carefully over a longer time. For more details about technical performance improvements, check out this great talk by Yuri Artamonov, and stay tuned for more articles.
While we worked on the technical side, we also decided to take a different approach, addressing the perceived performance of the IDE.
Phased sync indexing
The IDE does not necessarily need to be technically improved in order for users to experience it as faster. As long as they are able to start working sooner, they will perceive a performance improvement. From our research, we know that a lot of users think that the IDE is ready for work when they can see the project structure and proper code highlighting. So that is where we concentrated our efforts.
The IDE has to perform several crucial steps before highlighting and navigation can work, but we wondered whether these steps really need to run in series, sequentially.
Some promising experiments in version 2023.3 showed that splitting the process of syncing and indexing into several stages and running them in an asynchronous manner allowed users to get to their code much faster.
So as a first step, we made IntelliJ IDEA start indexing the files in the project directory before it actually gets the project model from the underlying build tool like Gradle or Maven. The downside of this was that unnecessary files got indexed, and after synchronizing with the build tool, reindexing was required. Nevertheless, the overall time-to-code (including both the sync and the full indexing) decreased significantly, becoming up to 1.5 times faster on big projects, according to our test suite.
Without the project model loaded, however, it was not possible to properly build relationships between parts of the project, show the correct project tree, highlight, or provide navigation.
To address this problem, we implemented what we call phased sync. Instead of requesting the full project model from the build tool all at once, we had the IDE get the model in phases. Currently, there are two.
Phase 1: Skip resolving dependencies
In the first phase, which happens as early in the project-loading process as possible, IntelliJ IDEA does not resolve dependencies or connect to the internet. It simply provides a model that’s accurate enough to allow the IDE to show the project tree, index the most necessary parts of the project, and provide some essential smart features.
Obviously, some dependencies might still be missing after this phase, causing resolution problems and cases where code gets marked red even if it is correct. The IDE is actually aware of these false positive errors, and it addresses them at the level of individual language support by suppressing errors that are caused by missing dependencies. It is also able to properly handle navigation attempts with respective messages. Since version 2024.2, this error suppression has worked for Java, and in 2024.3, the support was extended to Kotlin.
This first phase is especially fast in Maven because its static pom.xml configuration files can be parsed by the IDE without launching Maven, while still providing enough knowledge to build a very accurate model.
For Gradle, which is dynamic by nature and very flexible in its scripts, IntelliJ IDEA cannot parse scripts on its own at the moment – meaning it has to run the Gradle daemon. We sped up this process by only requesting sourceSets, the required language level, and other basic information from Gradle. However, with the Declarative Gradle initiative, we may be able to improve this dramatically in the future.
Phase 2: Download and resolve dependencies
During the second phase, IntelliJ IDEA downloads all plugins and dependencies, properly resolves all configurations, and provides a fully accurate model for the IDE to work with. Indexes are then updated based on the full model.
With this two-phase approach, you get an almost fully functional version of IntelliJ IDEA much faster, while dependencies are still downloaded and resolved.
As an additional improvement, we’ve made it possible for a lot of features to work while indexes are not yet ready, further decreasing time-to-code. The most important ones are code highlighting within individual files, partial code resolution, and run configurations. Of course, having the underlying index ready makes these operations even faster, but they work reliably even with only partial indexes.
The result
From a bird’s eye view, the entire process now looks like this:
How do we know it was worth it?
Depending on the project, time-to-code (or, more precisely, time before full highlighting in the schema above) in our test suite is up to several times faster the first time a project is opened.
Here is an example showing how fast the project tree is displayed and highlighting is enabled in a project where it used to take tens of seconds to access these features.
Surveys show that around 30% of our users think IntelliJ IDEA 2024.2 allows them to get started with coding faster than before.
What’s next?
While not all of IntelliJ IDEA’s features are available while indexing a project, the two-phased syncing approach has definitely sped up overall startup performance, giving you a faster time-to-code. Additionally, we’ve made many actions compatible with having no index or only a partial one available. And in the version 2024.2, around 10% of our users do actually write code before the entire sync and indexing process finishes. But we’re not done yet.
Our aim now is to improve phased syncing, especially with Gradle, to improve time-to-code even further. We’re also updating the UX of working with your project while dependencies have not yet been resolved.
In the meantime, we hope you enjoy the fruits of the labor we put in improving time-to-code. Download the latest version of IntelliJ IDEA and let us know what you think!