Building an Enterprise IntelliJ Plugin for Android Developers

Pierce Johnson
Lyft Engineering
Published in
6 min readJul 20, 2021

--

The Android engineering team at Lyft exclusively uses IntelliJ to develop new Android features (Android Studio is on the horizon, but that’s for another article). The IntelliJ platform offers a powerful SDK that enables developers to build plugins that enrich the IDE’s feature set, and a few years ago the team decided to take advantage of this SDK by building a plugin. There were a few goals in mind:

  1. Improve developer efficiency — Provide tools to incrementally improve the developer experience.
  2. Encourage best practices — Generate templated code that conforms to the team’s best practices.
  3. Improve developer sentiment — Improve the mobile development experience at Lyft.

If these goals resonate, continue on! Developing an enterprise-level IntelliJ plugin is easier than one might think. This blog post should have everything required to get started with writing an enterprise-level plugin.

Does the team need an IntelliJ plugin?

Before building a plugin, it is important to evaluate whether or not the team needs one! The following questions would be worthwhile to ask:

Are operations performed repeatedly during development?

Common operations may include generating test files with a specified internal format, implementing common architectural components, etc. If any of these ideas feel familiar, a plugin may be right for the team.

Android Rider and Driver line count (in millions) over time

Is the team large enough to benefit from a shared IntelliJ plugin?

Engineering velocity initiatives become more and more important as the size of the organization grows. Each improvement can be compounded with every developer, and at some point the efficiency improvements outweigh the maintenance cost of the tools.

Where to start?

There are a few general steps required to begin building an enterprise-level IntelliJ plugin:

  1. Conceptualize— Evaluate what kinds of things can be automated.
  2. Develop — Create the project in IntelliJ and develop the feature set.
  3. Distribute — An IntelliJ plugin needs to be installed in the IDE and may need to be updated regularly.
  4. Host — Publishing the code remotely makes it easier for other engineers to contribute improvements.

Conceptualize

First, it is useful to begin by developing ideas for the plugin’s feature set. What sorts of things could be automated or made more efficient? To jump start ideation, below are some features that the Lyft engineering team has developed internally.

Generate architecture components

The Android team has a wide range of internal architectural components that are used to develop new features. There is some manually-written code required to utilize some of these components, so the team added a feature to the plugin to generate this code instead.

Create Android modules

Lyft’s mobile codebases are heavily modularized. A new Android module is created for nearly every new feature. Many apps use Gradle to manage builds, but Lyft uses Facebook’s open source Buck (and soon, Bazel), which works very well for large modularized codebases. Unfortunately, however, IntelliJ’s built-in feature to add new modules to the codebase does not work for our team because it relies on Gradle. Thus, it has been replaced by a custom module generator that utilizes our internal module structure.

Parse XML layouts and generate view bindings

A few years ago, Butterknife was removed from the codebase to improve compilation times, but in doing so developer velocity decreased. To address this, we introduced a new feature to parse a layout file, find the view IDs, and add view bindings to the associated Kotlin View Controller in our custom internal format.

Support Dagger 2 migration

Like many other companies, Lyft migrated its Android dependency injection framework from Dagger 1 to Dagger 2 several years ago. Migrations like this can be long and arduous processes in large applications with many custom architectural components. The IntelliJ plugin was used to facilitate the migration through the introduction of a couple of new tools.

Copy Dagger 1 module to Dagger 2 module — The plugin copies the Dagger 1 module into a new Dagger 2 module.

Generate parent dependencies — In Dagger 2, child dependencies need to declare dependencies provided by a parent. This tool generates the Dagger interface for these dependencies.

Develop

At a high level, the development process for an IntelliJ plugin is very similar to Android development. It shouldn’t be surprising to hear that plugins are written in Kotlin right within the IntelliJ IDE. Furthermore, debugging Android applications is nearly the same as debugging IntelliJ plugins, but instead of opening up an emulator by pressing the green play button, a fresh instance of IntelliJ is opened instead.

To get started, run through IntelliJ’s own documentation: Creating your First IntelliJ Plugin. They provide a great starter kit to help understand how plugin development works. Run through the steps to get a project set up and something to play around with.

Once a basic project is set up, start implementing the feature set. Here are two resources that are useful to get started implementing features:

  1. Review the IntelliJ SDK Dev Guide to become familiar with the general concepts behind the IntelliJ SDK.
  2. Take a look at other popular open source IntelliJ plugins hosted on Github, and see if there are relevant projects for a company’s internal use case. Perhaps the concepts from an open source project can be leveraged to build one of these features.

Distribution

Once a plugin is created, building it will generate a JAR file, and this file can be manually installed in the IDE by navigating to Preferences > Plugins > Install Plugin from Disk. However, this process needs to scale to the entire team, so installation should be automated.

Installation script

With a team of 60+ Android engineers at Lyft, distribution was quite challenging. As an MVP, the team wrote a script to auto-install the JAR onto each developer’s machine. The specific steps were:

  1. Host the plugin’s JAR file in a remote artifact repository (Lyft uses Artifactory).
  2. Write a script to download and install the plugin in every engineer’s IDE.
  3. Add a hook to execute the script.

Hosting the JAR is relatively straightforward. Every time a new feature is developed, the code is built, and the new JAR file is uploaded to Artifactory.

The script is quite a bit more interesting. IntelliJ plugins can be manually installed by simply dropping the corresponding JAR file into the ~/Library/Application Support/<Your IntelliJ Folder> directory. Knowing this, the team could write a shell script. Here is the pseudocode:

installPlugin() {
# Find all IntelliJ directories by iterating through:
~/Library/Application\ Support/*Idea*
# If the local Lyft IntelliJ plugin version is different from the
remote version
# Download the new plugin JAR and store it in a temporary
file
# Delete all old plugins from each IntelliJ directory # Copy the new plugin to each IntelliJ directory # Delete the temporary plugin file
}

Finally, a hook was added to install the plugin every time a developer pulls and builds the Lyft codebase on their local machine.

# Executes installation function in shell script
installPlugin
Full code for the shell script

Host

Finally, the team created a repository and hosted the code on GitHub, empowering other Lyft developers to contribute new features to the plugin.

Impact

The plugin has substantially improved the Android developer experience at Lyft, with most engineers using the plugin every day. The initial feature set has grown from a single feature to over ten features with contributions from many engineers. Most recently, the team has even introduced analytics to more concretely analyze the impact of the plugin on developer velocity. It will be exciting to see how the plugin continues to evolve and improve the developer experience in the near future.

Conclusion

Improving developer velocity/sentiment and encouraging best practices is an important part of any engineering organization. The IntelliJ SDK offers a useful tool that can be used to improve the developer experience at any mobile-facing company, and hopefully this article provides enough detail to get started creating, hosting, and distributing an IntelliJ plugin across any Android engineering team. A huge thanks to our client tooling team and mobile infrastructure organization for their continued investment into the developer experience here at Lyft!

As always, we are hiring! If you’re interested in joining the team, check out our careers page.

--

--