I spend most of my days at work on powerful IDEs like Eclipse or Netbeans, tools so advanced in functionalities that their feature lists span over several pages. Their power, however, has its own drawbacks: their memory consumption is measured in the gigabytes, and running them on underpowered computers is the most frustrating of experiences. Issues that any Java developer on Earth will have to face, sooner or later.
Having grown frustrated by Eclipse being too slow on my 4 years old work laptop (I will not comment on this), I decided to drop the IDE for a while, switching to an extremely powerful editor that offered me the one thing that matters the most to me: blazing fast navigation between different source files.
Of course I knew I would miss some of Eclipse’s advanced features but I wanted to give it a try, especially since Andraz’s post left me with a bit of curiosity: how much the tools we use affect our abilities? And why IDEs are so used by desktop developers while they are not so popular with web frontend developers who generally use scripting languages?
A commonly accepted explanation is that it is easier to write IDEs for static languages, when lots of information is known at compile time. It is easier to extract information from the code and use it to build powerful and useful features.
However, after a few weeks of experimentation, I ended up with a slightly different point of view on the whole matter: the popularity of IDEs for Java encouraged coding conventions would not be so widely accepted if the majority of coders used a plain text editor to edit their source files. Those conventions grew so popular that that today Java appears to be designed to be used with an IDE. Let me give a few examples.
Autocompletion harms API consistency
If we want to know the length of a collection (as an abstract group of objects) in Java, the code we have to write will be quite different depending what object we are working on. It can be
length if we are working on an array,
length() if we are working on a String,
size() if we are working on a Collection (from the Java Collections Framework).
Compared to Python, where the length is always retrieved by calling
len(_), or to Ruby, where each enumerable type offers a
length method, Java APIs often look disorganized and inconsistent.
The cost of inconsistent APIs is mitigated by auto-completion: a quick glance at IDE suggestions helps us pick the right method by skimming a list of method signatures and, occasionally, reading the inline documentation.
Coders who are not used to this kind of support put a lot of value in predictability and consistency: if some popular library has a specific interface that resembles what their new library is going to do, they stick to the same interface, without reinventing the wheel every time. This means that switching from a library to another is almost effortless, and it is kind of fun to think that Ruby’s mixins achieved what Java’s interfaces could not, but that is another story.
Following the same pattern, iterating over an array, a String and a List was done quite differently in Java, although this has been mitigated by the introduction of the for each construct in Java 5. And yet again, dynamic languages tend to be very consistent and I believe that is not due to duck typing itself, but also to lack of an Intellisense-like autocompletion for scripting languages pushes coders to rely on habits, implicit knowledge and API conventions they learned by working with other tools and libraries.
Packages and imports work great until we need to manage them
A widely accepted convention among Java coders is to use fully qualified imports in source files, because this improves readability and makes dependencies on other classes explicit. This means that, even though there are a few notable exceptions of packages for which a general (
*) import is considered acceptable, such as
import java.util.*; import java.io.*;
generally Java sources start with a long list of imports, with one line per each class outside the current package.
The rationale behind this convention totally makes sense, and it is effortless to coders as any decent IDE is able to do a great job in managing imports automatically. But guess what? Once you are using a text editor to work on your source files, all the burden of taking care of imports is on your shoulders.
Now, the choice to make Java packages correspond to the way classes are arranged on the filesystem is a strong one. Compared to C++ namespaces, Java packages tend to be more structured: a complex project is likely to contain several nested packages with several a few classes each. This means that, even if you contemplated the option of using the
import * notation, you would still need to do some menial work to manage imports.
I would not say that packages are a bad idea, but being forced to manage them was probably one of the things that made me miss Eclipse the most.
Code generation increases verbosity
A lot has been written about Java being too verbose, and I tend to agree with that argument. The design of Java privileges readability and non-ambiguity over syntactical conciseness, making it fairly easy to learn and use. Overloading operators, for example, is not allowed as it was one of the most common source of surprises in C++.
However, verbosity is not just about syntax: many of the common conventions in Java require developers to write lots of boilerplate code with little informative content. Take the example of accessor methods: for each property we add to a Java bean we have to write the corresponding getter and setter methods. The task is so repetitive and meaningless most IDEs offer the possibility to generate automatically the code for accessor methods.
The same thing in Ruby is extremely simpler: the
attr_accessor method addresses the same problem in an incredibly concise way. It uses metaprogramming (which itself is not achievable in Java) to accomplish its objective, but still it is an effective way to address the same issue.
The developer community influenced the evolution of the language before with (e.g. with the introduction of Generics), but the availability of code generation within IDEs has made Java developers much more tolerant towards verbose patterns. Would manually writing code for getters and setters be acceptable if we had no support from Eclipse? Personally, I do not think so.
Working with Sublime Text is great, it is incredibly fast and outperforms Eclipse in text manipulation. However, I unsurprisingly missed Eclipse when I had to debug, refactor existing code or inspect some complex call hierarchies in multiple projects, tasks for which an IDE is perfect.
What surprised me the most, however, was that I did not really miss Eclipse because of the benefits it gave me, but because it saved me a lot of nuisances that are caused by the way we write code in Java, by the conventions we chose ourselves. It is not by chance that Java does have plenty of IDEs and scripting languages do not; it is because of the way it was designed.