Saturday, January 14, 2012

Switching to IntelliJ IDEA

IntelliJ IDEA 11 came out recently, and after some frustrations with Eclipse I decided to give the Community Edition a try. To my surprise, I've decided to stay with it, and I thought it was a good opportunity to dust off the old blog to explain why. Maybe it will be useful to others who have been considering trying IDEA for a while but aren't sure if it will be worth their time.

Eclipse
First off, let me give Eclipse some credit. I've been more than satisfied with it over the past 6 or 7 years. I haven't had any major issues with Eclipse, but it's been a bit like death by a thousand stings. Most of the pain comes from a few poor quality plugins or good plugins not playing nicely with each other.

Eclipse's incremental compilation is incredible. It's probably the biggest feature that differentiates Eclipse from IDEA, both pro and con. The pro is immediate feedback. By the time you're ready to run your tests, Eclipse has done most of the heavy lifting and is ready to launch. However, that means the heavy lifting happens while you're editing or otherwise interacting with the UI, which can make the IDE feel less responsive at times. The JDT can also affect some plugins, which I'll get into later.

Enter IDEA
Later I'll list a bunch of small features that don't seem like much individually, but combined make a big difference. But first, the big differences.

Responsiveness
The biggest difference I've noticed with IDEA is its responsiveness in regard to user interaction. It feels lightweight and snappy compared to Eclipse. Best of all, there's immediate code completion with no keyboard combination to press and no waiting. This is huge. There have been several times I've cursed out Eclipse for having to wait a few seconds after hitting Ctrl Space. Especially in Scala code (more on that later).

Maven Integration
Surprisingly, IDEA has better Maven integration than Eclipse, despite Sonatype's great work on m2e. This is most likely an indirect result of Eclipse's incremental compilation. Because of this killer feature, Eclipse cannot delegate the build to Maven. That means m2e has to integrate the whole Maven build lifecycle into the JDT. This is called "project build lifecycle mapping" and the result is more configuration and plugin bloat. What's worse, if you're missing a lifecycle mapping it's an error by default: "Plugin execution not covered by lifecycle configuration". This was the straw that broke the camel's back for me.

Even the pom file editor is better in IDEA. For example, it will autocomplete Maven groupIds, artifactIds and versions for artifacts you don't yet depend on, which has saved me from going outside of the IDE (especially versions). Also, it has built-in completion for Maven plugin configuration parameters in the pom file which I've had to go to the web to look up in the past. "Navigate to Managing Dependency" lets you navigate from a dependency to its corresponding dependencyManagement section, even if its in a parent pom. Plus it has an "Introduce Property" pom file refactoring which is a time-saver.

Scala Integration
Scala integration in Eclipse is improving at a fast pace and soon may surpass that of IDEA, but for now I give IDEA the slight edge. It makes sense because Scala is in the same boat as Maven in Eclipse of having to integrate with the JDT. With Scala this is much more of a problem, though. The sheer amount of work that the Scala compiler has to do means incremental compilation of Scala code significantly affects the performance of Eclipse.

For the most part, Scala "just works" in IDEA, with most of the same features Java has plus the Scala-specific ones you'd expect. It even has a "Convert Java file to Scala" refactoring which is also known as the Ugly Scala Code Generator (TM), but it might save you some time if you're converting a project over. However, I have had some rare phantom errors in IDEA, meaning code marked red in the editor that compiles just fine. Also, since editor parsing is separate from compilation, these don't go away when you do a build or run tests like they might in Eclipse.

The Rest
This is the list of minor improvements which I've found useful. To keep it brief, I won't go into too much detail on each one, so I apologize if these are unclear. Many of these are examples of IntelliJ living up to its billing as an intelligent IDE because they require a deep understanding of your code.

  • Ability to search for actions (Cmd Shift A). I cannot overstate how valuable this has been in my transition. You can look up any action (refactoring, searching, etc.) and execute it while keeping your hands on the keyboard. It also displays keyboard shortcuts so eventually you won't have to look it up again.
  • You can limit the scope of any search (text search, class hierarchy, etc.) to only production code or only test code.
  • Highlights classes as being unused when they are only referenced by tests.
  • Colors tabs differently based on the nature of the class. For example, test classes are green.
  • Colors annotations differently to distinguish them in imports.
  • Rename refactoring prompts to rename constructor and setter parameters in addition to getters and setters.
  • Console output from each test is self-contained, but you can get to the full console output if you need it.
  • Cmd click navigation from a string literal to a file with the same name in the project.
  • "Go To Test Subject" (Cmd Shift T) from a test class navigates to the class under test.
  • Optionally warns you when you're about to commit code with warnings/errors, and can create a separate changelist for you with only these changes if you choose not to commit.
  • "Shelve commits" lets you put aside your current changes so you can work on the current revision (or a different one) if you need to.
  • Autocomplete within string literals.
  • When you rename a class, it prompts you to rename similar variable names where the class is being used.
  • Extract method also finds duplications with different method arguments. I used to manually "pre-refactor" to get this same behavior in Eclipse by first extracting local variables for the intended arguments.
  • Smart step-into when debugging so that you can step into a specific method on a line of code which calls many methods.
  • If you're editing a class file and add a reference to a class name that can't be resolved, there's a quick fix to "Add Maven Dependency".
  • Introduce parameter can move local variable initialization code from within a method to the method's callers.
And here's some features that look useful, but I haven't tried yet.

  • Analyze dependencies.
  • Analyze backward dependencies.
  • Refactor -> replace inheritance with delegation.
Conclusion
It's mostly a bunch of little things that I like better about IDEA. Any one of them wouldn't be enough to make the switch, but combined make me feel more productive even after a few short weeks.

I know it's just a matter of time until I buy the Ultimate Edition, and I'm ok with that. You win this round, JetBrains. ;)

3 comments:

  1. Great overview. I'm downloading now and will give it a try.

    ReplyDelete
  2. Here's a nice refactoring thing the IDEA does that I don't see in other Java IDEs.

    You are decoupling production code via dependency injection in order to unit test it. So you introduce a decoupling interface that holds everything that's making your unit test an integration test, say IPartner, inside the SUT.

    Then you add an final instance of IPartner in the SUT that is set to an anonymous impl of IPartner in the existing c'tor. You add an overload of the c'tor that allows you to inject an instance of IPartner from your test.

    Now you Extract Method around the stuff that's coupling you and giving you pain, such as constructors for collaborators and calls to static methods on other classes. You move the method from the SUT to IPartner instance and BAM!

    IDEA asks if you want to move the implementation to the anonymous impl of IPartner. Hellz yeah! Now we've kept production code working using the old non-DIP API and we've got the separation needed for our tests.

    ReplyDelete