Four years ago, after Google had announced “first class” support for Kotlin programming language in Android, I wrote a post titled Kotlin vs Java The Whole Story. This turned out to be my most controversial writing because, at the peak of Kotlin hype, I dared to put out the proposition that migration from Java to Kotlin might be a bad idea for businesses and individual Android developers. To say that I was surprised by the amount of (mostly negative) feedback that article generated is an understatement.
Evidently, all this is just water under the bridge at this point. Today, we know that Kotlin isn’t just “first class” language, but, as I predicted back then, it is the preferred language and the future of Android development. However, still, I’m curious to go back and review the accuracy of my original opinions and predictions. Therefore, in this article, I’ll discuss the impact of Kotlin programming language on Android developers’ productivity over the past four years.
Best Case Scenario For Kotlin
Following the framework proposed by Steve McConnell in his Code Complete book, I looked at the process of software construction as three distinct activities: detailed design, coding and debugging and developer testing. The relative effort of each of these steps as a function of project’s size is depicted in this chart:
Assuming that a switch from Java to Kotlin won’t affect “detailed design” and “developer testing” activities, I then assumed that Kotlin optimizes the “coding and debugging” effort by 10%. Many readers objected, saying that in their experience the speedup is higher than that. To these readers I suggested plugging their own estimate of productivity gains into the calculations and sharing the result with us, but, to my best knowledge, none of them followed up on that.
What I didn’t write back then, but will say now, is that the above chart kills the promise of Kotlin as “better Java” on the spot. It implies that even if Kotlin would be ideal (i.e. free of the negative effects we’ll discuss below) and would increase “coding” productivity by even whooping 50%, it wouldn’t make much difference in the grand scheme of things. After all, if you take into account all the activities required for successful software delivery, it becomes evident that a migration to a relatively similar programming language can’t yield any major benefit on its own.
At this point, some readers will be eager to conclude that I basically advocate for not switching programming languages ever. In the extreme, this line of thought culminates in a straw man of the form: “if it would be up to you, everybody would still code in Cobol”. This conclusion is incorrect because there are languages out there which are sufficiently different to affect more than just “coding” part of software construction (e.g. introduction of object-orientation). In addition, different environments can have vastly different characteristics, calling for different programming languages being used. However, if we look at Java and Kotlin in the context of Android development, none of the above conditions applies. These are relatively similar languages used in the same environment. So, sorry to break it for you folks, but “30% more productive” is just not feasible in this situation.
All in all, I think that choosing 10% boost during “coding and debugging” activities as the best case scenario for Kotlin was very reasonable. I doubt anyone does much better than that, even if they end up writing 50% less source code.
Back when I wrote my original article I hadn’t actually learned Kotlin, just skimmed the docs and the basic examples. Kotlin looked very much similar to Java, so, when estimating how long it will take an average developer to switch from Java to Kotlin, I used 5 days as an estimate.
Today I know Kotlin and work with it, therefore I realize how overly-optimistic I was back then. It took me much longer than 5 days to get up to speed with Kotlin and even today I keep learning and re-learning some of its features (for example, just last week, I spent two hours on reified type parameters). I think that my original estimate wasn’t even close to the actual number and today I’d say it takes about 10 days for an average developer to switch from Java to Kotlin.
By the way, I discussed a specific imaginary example of experienced Java developers switching to Kotlin, but the ramp-up overhead applies to completely new developers as well. Since Kotlin is more complicated than Java, even developers who learn just one language from scratch will need to pay the price of ramp-up overhead. Speaking of languages, one of the most common questions on Android forums is “should I learn Java or Kotlin?“. As far as I can tell, the most common answer is “you’ll need to learn both”. In other words, even four years later, Kotlin is still not exactly self-sufficient in Android development.
All in all, I was off with my estimation of ramp-up effort. However, the direction of the error made Kotlin look better, which was in line with my original desire to make the estimates as favorable as possible to Kotlin.
Developers Skill Loss
Here I used Cocomo2 model’s “Language and Tool Experience” cost driver to estimate the overhead caused by developers having less experience with a new language. Given Java and Kotlin are similar in many ways and share the same ecosystem, I assumed that an experienced Java developer will “magically gain back” half of their Java experience when switching to Kotlin. In other words, for example, Java developer with one year of experience will become Kotlin developer with half a year of experience after the initial ramp-up.
Considering this aspect now, I think my estimation was reasonable. Java and Kotlin indeed share many tools and the general ecosystem, but the differences are still substantial. It’s not a coincidence that so many developers and projects reported “writing Java-style Kotlin after the migration for a while”.
So, I think that my analysis of this aspect was accurate.
Language and Tools Stability and Maturity
In this section of the original article I tried to estimate how the fact that Kotlin is much less “mature” than Java will affect Android projects’ completion time. In retrospect, all the arguments about breaking changes, release cadence, etc. turned out to be pretty accurate, but largely irrelevant given the scale of the real problems that accompanied Kotlin’s adoption.
The most critical drawback of Kotlin in Android is build times. It’s not a secret that build times are major productivity killer on bigger Android projects and it’s also not a secret that Kotlin is worse than Java in this regard, but, for some reason, the real scale of this problem is rarely ever discussed.
In 2019, Uber published the results of their benchmarking project of Kotlin and Java. Their findings suggested that for pure Kotlin or mixed projects, clean build times can be several times higher than those of pure Java. Later that year, Jessie Wilson shared the metrics of OkHttp library before and after migration to Kotlin and reported 4x increase in clean compilation times. Evidently, the most interesting metric is not just compilation time or clean builds, but incremental builds of real projects. Unfortunately, I couldn’t find any good resources on this topic (this widely cited article is fundamentally flawed because it compares apples to oranges). In 2018, I performed a very basic benchmarking of Kotlin’s introduction into Java-only project and found ~8% increase in incremental build times. However, the test project was very small and single-module, so I’m sure that build time overhead in big real-world codebases is even higher.
It’s not a coincidence that the over-modularization hype from several years ago started shortly after introduction of Kotlin. Back then, the tools had been even worse than in 2018-2019, so projects that jumped onto Kotlin hype-train quickly discovered that it’s not all rainbows and unicorns (though not many of them admitted that). Modularization was the only solution (aside from rolling back to Java), so even relatively small projects embarked on this questionable journey. The result? Massive over-modularization and waste.
The situation with build times improved gradually over the years, but even four years later, Kotlin is still inferior to Java in this regard.
The second biggest Kotlin’s drawback is debatable because there are quite a few candidates. However, for me, it’s the auto-completion inside Android Studio because it’s so much worse in Kotlin than in Java. How bad Kotlin’s auto-complete is? Well, in some cases, it can’t guess my intent even after I spell the full name of the class explicitly:
In addition to being just dumb, auto-completion with Kotlin is noticeably slow and “unstable”.
Unstable means that when you add another character to the search string, the suggestions can change their places. This is super annoying because if you type too fast, you risk ending up choosing the wrong suggestion. When I write Kotlin exclusively for a prolonged period of time, my typing speed adjusts to this limitation and I don’t notice it. However, when I switch to Kotlin from a long Java streak, this happens constantly. I guess that I’m subconsciously slow myself down to wait for auto-completion when I write Kotlin code. That’s very bad.
Metaphorically speaking, due to bad auto-completion, switching from Java to Kotlin feels like tying one hand behind my back. Given how bad the situation is, I don’t understand how anyone can claim any productivity benefit of Kotlin over Java.
So far, I listed what I see as the most problematic aspects of using Kotlin in Android today. These are major problems. Needless to say that four years ago the situation was much, much worse in every measure. However, these aren’t the only problems and the above discussion is far from being exhaustive. Even today there are many additional drawbacks to Kotlin (e.g. crippled “extract interface” refactoring in Android Studio, very slow syntax highlight, build issues, etc.).
All in all, my estimate of the additional overhead related to Kotlin’s immaturity was way too optimistic. The real cost turned out to be much greater than I anticipated and some of the problems that caused that have yet to be solved.
Now I’d like to discuss a specific feature available in Kotlin: Coroutines framework.
If you read the comments to the original post, you’ll notice that several readers brought up Coroutines as a factor that I missed from my calculations. In essence, four years ago, quite a few developers thought that Coroutines will be a major step forward in terms of developers’ productivity. I didn’t know much about Coroutines back then, but I understood the fundamental challenges of concurrency, so I couldn’t share the enthusiasm. I thought that completely new concurrency model will surely involve many trade-offs and require much time to learn. Unfortunately, my pessimistic predictions in this context turned out to be more accurate than the optimistic expectations of the commenters.
Coroutines is the most complex concurrency framework I’ve ever worked with. It makes writing trivial concurrent code look simple, but, in practice, introduces more ways to shoot yourself in the foot than I can count. Marketing campaign for Coroutines was (and still is) very misleading, claiming simplicity where there is none, and promising “simple cancellation”, which is an oxymoron.
I’m pretty sure that many developers will disagree with my assessment of Coroutines. That’s fine and I might be biased. However, given the fact that I published professional-level, 10+ hours courses about both “traditional” multithreading in Android and Coroutines, I’m in relatively unique position to evaluate and compare the complexity of all these approaches. Consider even this simple pair of metrics: in my multithreading course, I start with the hardware and then “build” the entire theory of concurrency in Java in 12 hours, and then explain the basics of several concurrency frameworks; in contrast, in my Coroutines course, over the same 12 hours, I only explain how Coroutines work, and even then I intentionally left out some aspects (e.g. Channels) and didn’t even mention Flow.
The above metrics suggest that there is as much nuance and complexity in Coroutines framework alone as is in the entire theory of concurrency in Java. And make no mistake: even if you decide to use Coroutines exclusively in your app, you’ll still need to learn about Java concurrency. Otherwise, it’s just a matter of time before you start planting concurrency bugs in your code.
As a creator of a course about Coroutines, it’s in my interests to tell you that Coroutines are amazing. But, instead, I choose to tell you the truth.
So, was my post from four years ago totally accurate? Of course not. My estimations were off in many cases and I missed some of the more important aspects. However, all in all, I feel that the methodology that I used was solid and the analysis yielded relatively accurate predictions. At the very least, my predictions turned out to be much closer to the real state of the matters than the far-reaching marketing slogans and expectations of many developers at the time.
If I sum everything up then I arrive at a depressing realization that introduction of Kotlin into Android ecosystem caused a huge waste. After I had written my original article many Kotlin fans accused me of “spreading FUD”, but, in hindsight, it turned out that I was still too optimistic. The lion share of this waste fell onto the shoulders of the early adopters (individuals and companies), but even new developers today (who might not even know Java) still pay “Kotlin tax”.
I’m sure that many readers will think about Jetpack Compose and Kotlin Multiplatform as potential justifications for having Kotlin in Android. However, in my opinion, even if both of these technologies will succeed, the payoff will be so delayed in time that effectively none of the current developers or projects will get return on their investment (let alone the early adopters from several years ago).
Finally, said all the above, it’s clear that Kotlin, Coroutines, Compose and, maybe, even Kotlin Multiplatform are the future of Android development. Therefore, whether I like them or not, I’ll be forced to learn them one day (or have already been forced). However, this doesn’t mean that I need to jump on every hype train the moment it arrives. Just like with Kotlin, there is no rush to adopt any of these technologies. And if you do decide to become an early adopter, then it’s totally fine. After all, we, developers, like playing with new ideas and learning new ways to work. That’s what makes our industry innovative. However, be honest with yourself and your employer about the motivations behind becoming early adopters and the most probably outcomes.
As usual, thanks for reading. You can leave your comments and question below.