Kotlin: A massive leap forward
This article summarizes the first part of my research about the impacts of Kotlin on companies and individuals that have existing Java investments.
Since there are so many impacts, this first part will only touch on how Kotlin affects reading and understanding code since this is where we spend 90% of our time. Although this sounds high, it takes many hours to hunt down a defect only to realize that a couple of lines need to be tweaked. A similar process takes place when adding new capabilities to an existing code base, especially when the changes impact existing functionality.
Ratio of time spent reading versus writing is well over 10 to 1 (source)
Good programmers spend much of the other 90% thinking, researching, and experimenting to find the best design (source)
As a brief background, I‘m a senior developer who enjoyed working with Java and used it for my personal side projects as well. Although line count isn’t a good measure of productivity, I could exceed 1000 lines of clean code per day whenever I had a clear plan of attack. I’m only saying this to point out that from my perspective, I didn’t feel like I was missing anything. My investigation began when Google announced first-class support for Kotlin.
Introduction to Kotlin
Kotlin is a free and open-source programming language designed by JetBrains. It’s been in development since 2010 and has been ready for use in production since February 2016. Many highly regarded companies have already incorporated Kotlin into their production applications. JetBrains mentioned that adoption is roughly a 50 / 50 split between Android and back-end JVM server applications. Adoption rates are so high that it’s predicted to overtake Java for Android development within a year. This will make an impact on the type of skills that will be in demand.
Kotlin compiles to Java bytecode and runs on the JVM so it’s suitable for all environments where Java is used. This includes Android as well as back-end server applications. Kotlin is also interoperable with Java in both directions. This allows companies to leverage their existing investments in Java as well as the large collection of open-source Java libraries without needing to re-write anything. Kotlin classes can work alongside Java classes and although not required, Java classes can even be automatically converted to Kotlin which enables a very gentle learning curve. When working inside a Java class, Kotlin classes are exposed in a Java-friendly way and vice versa.
Kotlin can also compile to native (eg. for iPhone), JavaScript or WebAssembly for the front end, and even Gradle build scripts can now be written in Kotlin. The idea of implementing the back-end, front-end, and build script all in one language is very promising. Although Kotlin on the front end sounds strange at first, the benefits are similar to why companies choose TypeScript but with many more guarantees.
You can even define domain-specific languages (DSLs) which can guarantee correctness at compile time for custom patterns or formats. For example, you could define DSLs for SQL queries, HTML, or XML which prevent you from creating malformed SQL, HTML, or XML content. As a side effect, you also get IDE hints, auto-completion, and refactoring capabilities which aren’t usually available outside of programming languages.
Kotlin also supports writing business logic as multi-platform libraries that can be shared across the back-end, Android mobile app, or even web front-end. A common best practice is to perform input validation on both the front end and back end so being able to define the validation and utility functions in only 1 place is nice. The duplicate development and test automation effort for each platform can now be re-used instead. This also ensures that all platforms have consistent business logic behavior.
Framework for quantifying productivity impacts
Before making any numerical claims, we’ll first define a framework to enable us to quantify productivity impacts.
While faster typing can allow faster coding, typing speed doesn’t have a meaningful impact on productivity since developers can’t solve problems as fast as they type. Improving an area only provides meaningful productivity benefits if it delayed some other process. Thus, eliminating a bottleneck and using the freed-up time to perform more duties improves productivity.
Additionally, most developers are less productive in the last few hours of the day due to mental fatigue. Our thinking process slows down as we get tired without realizing it. Even if the freed-up time isn’t used to perform extra duties, this reduces the cognitive burden by providing breaks or by reducing the intensity of tasks. This allows developers to maintain their level of productivity for longer periods. The net effect is that developer productivity improves whenever the effort is reduced.
The available productivity improvement depends on the bottleneck overhead and how much we can reduce it. As an example, if an action takes 60 minutes to complete due to a 40-minute bottleneck (40 / 60 = 67% overhead), eliminating that bottleneck (100% reduction rate) bumps our available productive time from 20 minutes to the full 60 minutes so we could potentially achieve 3 of these actions in the same amount of time. Alternatively, if we reduce the overhead by 75% (from 40 down to 10 minutes), productivity could double in this example. Generalizing these examples, we have:
We should note that if we measure productivity by the output of our coding actions, any development improvements directly impact the rate at which we produce value for customers. That is, doubling the productivity of our development actions can result in a doubling of the number of features delivered or defects fixed.
Kotlin code size and complexity
Converting a Java application to Kotlin is expected to reduce the line count by 40% according to JetBrains. Note that this isn’t accomplished by jamming more code on each line as the average line length is also reduced. Combined, this means that the total amount of code can be reduced by over 50% when leveraging the capabilities of Kotlin.
There are three main reasons for the code reduction. The first reason is due to a more concise language (eg. Kotlin data classes can replace 50 line classes with a single line). The second reason is that the Kotlin standard library enhances existing Java classes with extensions that trivialize common usage (eg. splitting a collection of items based on some custom condition or checking if a collection contains an item with certain qualities etc.). The last reason is very surprising. Kotlin enables higher levels of code reuse that are completely impossible in Java even when using Java 8 features. Although both languages are Turing complete, Kotlin allows you to extract more reusable patterns than Java allows.
As an example that’s not possible in Java, Kotlin allows you to use the return statement inside inlined lambdas so that it returns the same way that it would return from something like a for-loop. In Java, returning from a lambda is misleading as it doesn’t return from the current function that you’re in. The high-level meaning of the return statement varies in Java based on context (return from the method vs. return from this chunk of code). This Kotlin capability almost seems like you’re defining new language constructs which makes your utility functions very natural to use. As an example, something like the try with resources capability that we needed to wait for until Java 7 can be accomplished with a simple utility function in Kotlin. This allows you to write natural-looking business logic while abstracting away common patterns such as exception handling or acquiring & releasing locks. Better still, unlike the scalability concerns that you would get from some Java 8 features (eg. creating lambdas in a loop), inlined lambdas in Kotlin don’t have any performance or memory overhead.
Many best practices and design patterns have been incorporated into the language so you never need to implement them yourself. This also eliminates many categories of defects. Some examples are singletons, delegation, data classes, etc. Java requires you to take the time to read, understand, and verify that the patterns were implemented correctly whereas many of these are part of the language and guaranteed to be correct. These are read as concepts instead of as a series of instructions the way you would in Java. Additionally, many Java anti-patterns were eliminated which removes the cognitive overhead of needing to watch out for them. There are additional guarantees which reduce the number of assumptions or considerations that need to be made when reading through code (eg. can this Boolean variable ever be null and would that be accidentally interpreted as “false”?).
So in fact, the shorter Kotlin code is even easier to understand compared to Java code with the same behavior. This is because Java code needs to be read and mentally verified whereas Kotlin trivializes many concepts. One way of thinking about this is that reading Java code requires you to verify lower-level instructions whereas reading Kotlin code requires you to verify the correct use of concepts so it’s closer to behavior-level thinking. Also, the extra patterns that can be extracted compared to Java only need to be considered 1 time instead of re-evaluating them in many places.
Therefore, converting a Java application to Kotlin and taking advantage of Kotlin features reduces code size by 50%. The code is also much easier to understand because the extra Kotlin capabilities allow you to think using higher-level concepts. The possible states of the application at any given point are also reduced which reduces the possibilities that need to be considered thus reducing the cognitive effort. Combining the code size reduction with the readability effort reduction, I found that the time it takes to read and understand code is cut in half. This allows us to start producing sooner so productivity is impacted. Thus, Kotlin enables us to be 1 / (1 - 0.9 * 0.5) = 1.8 times more productive since this is where we spend over 90% of our time.
Realizing that the creation part of our time is also reduced, it’s easy to see how Kotlin could double productivity from these impacts alone!
I also noticed that since my code is closer to the concept level in Kotlin, this results in cleaner code so future maintenance overhead is also reduced.
I was honestly shocked at my productivity after becoming familiar with the language as I didn’t expect it to have a measurable impact (I was mostly thinking about reduced defect rates). It was only after completing a large task in Kotlin that I realized my career outlook would be forever changed. As much as I enjoyed working with Java in the past, having experienced such a pragmatic and productive language, I just can’t allow myself to be held back in such a severe way now that I see what’s possible.
As surprising as these results are, the impact on defect rates is much more shocking especially given the amount of time and budget that’s spent in that area (hint: many more categories of defects are prevented in addition to null safety). Another surprise is that Kotlin applications in general are more secure than Java applications (I might write more on this later).
With the introduction and productivity-measurement framework out of the way, the second part has space for concrete examples.
Follow and subscribe to be notified of my next article.