Feedback - Kotlin on the server-side - the good, and the bad

Feedback of using Kotlin on the server-side development after years of experience in Java

Background

I've used Java as my primary server-side programming language for years. Before I joined Qovery, I have worked on a cloud-native project at SAP, where Java was the default choice to bootstrap new microservices, besides it's known limitations and cumbersomeness.

JVM ecosystem, decent performance, and stability make Java a common choice in enterprises. From a developer's point of view, all of these come with the cost of working with old language, lacking some of the essential features that newer languages provide (which Java tries to fix with the recent changes and more rapid, 6-month release cadence).

Convincing colleagues to consider a new language (like Kotlin) was impossible, especially after taking our negative experiences with Scala. That's the main reason why my experience with Kotlin before changing the job was limited to playing with it in micro side projects and reading articles.

In April, I started working at Qovery, a container as a service platform where a considerable part of the backend code is in Kotlin. In this post, I'll describe the last months of my journey from Java to Koltin, my experiences and thoughts on this process, and Kotlin as a server-side programming language.

From developer experience point of view, Kotlin is a significant improvement, especially if you jump from 'old' Java - Patryk J.

My feedback

From Java to Kotlin

For me, a Java developer with basic knowledge of other languages (like JS, Go, and other popular programming languages) moving from Java to Kotlin was natural. It was more like a gradual transition/upgrade rather than jumping into something new. Most of the right parts from Kotlin that are missing in Java can be found in other programming languages. If you are not limited to just one language (Java), there will be little in Kotlin that will surprise you (for the people thinking the transition from Java to Kotlin is hard - I recommend trying to use Rust without reading the Rust Bible twice - you'll change your mind quickly.).

Reading/Writing Kotlin code

Reading Kotlin code came without any efforts. If you can't guess what the 'new' syntax means, all you need to do is take a quick look at the basic syntax page, and you are good to go. Writing idiomatic Kotlin code, however, is a bit trickier. After years of using Java, you may have habits that will lead you to write Java-like Kotlin code. Writing idiomatic code, at least in my case, requires more time spent with the language. I found this document invaluable to speed up this process.

How did I ramp up on Kotlin?

Getting ready to use Kotlin did not require more than spending a few days to get used to the codebase I would later work with and slowly reading the Kotlin documentation in the meantime. I believe this gives you all the basics you need to be productive with the language - proficiency and expertise will come with time.

After a few months

After switching from Java to Kotlin, my first impressions were that I deal with a modern programming language. Pascal notation, type inference, extension functions, no semicolons, immutable collections, coroutines, channels, and more - this all added to a feeling of using a powerful, modern language (even though most of the concepts are quite old, borrowed from other languages, you can see similar trends in other new, modern programming languages).

Another thing that I could quickly notice is the improvement in code readability (in the majority of cases) and conciseness. The most striking, simple examples are, of course, data classes and null checks, which cost much more work (and lines of code) in Java.

Data Classes (Kotlin vs Java):

Kotlin
data class Person(var name: String, var surname: String, var id: String)
Java
public class Person {
private String name;
private String surname;
private String id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (name != null ? !name.equals(person.name) : person.name != null) return false;
if (surname != null ? !surname.equals(person.surname) : person.surname != null)
return false;
return id != null ? id.equals(person.id) : person.id == null;
}
@Override public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (surname != null ? surname.hashCode() : 0);
result = 31 * result + (id != null ? id.hashCode() : 0);
return result;
}
@Override public String toString() {
return "Person{" +
"name='" + name + ''' +
", surname='" + surname + ''' +
", id='" + id + ''' +
'}';
}
}

Null checks (Kotlin vs Java):

Kotlin
fun main() {
val outer = OuterClass(null)
print(outer.inner?.someValue ?: “DEFAULT VALUE”)
}
Java
public class Example {
public static void main(String[] args) {
OuterClass outer = new OuterClass(null);
if (outer.inner != null && outer.inner.someValue != null) {
System.out.println(outer.inner.someValue);
} else {
System.out.println(“DEFAULT VALUE”);
}
}
}

After some time of working with the language, I noticed another thing - the null safety, in fact, does make the difference. It does not solve all the issues with NPEs, but it drastically reduces this problem. Most of the time, when I get NPE is during development when I intentionally cut corners due to my laziness. After the code is ready to go to production, it's improbable to throw an NPE.

Java-Kotlin interop

Java-Kotlin interop is excellent. You can basically use anything you would use in plain Java. Sometimes you may fall into a pitfall, you need to take care of nulls (they may come from Java libraries, which are not null-safe) or may need to use a plugin to make a given framework work properly. Still, in general, it's very good interop between two languages. You can even copy-paste Java code, and IntelliJ will convert it to Kotlin on the fly - it works quite good; most often, it produces working code - from time to time, you may need to correct something manually.

Java to Kotlin

Automatic Java -> Kotlin code conversion

The good and the bad

Why you should consider it

The list of good things about Kotlin is very long. I'll name just a few that are my favorite, and those alone should justify at least considering using Kotlin for server-side programming on the JVM:

  • null is a separate type - String (non-null) and String? (nullable String) are two different types in Kotlin. If you don't mess with the compiler, you are pretty much safe from null pointer exceptions. The less 500 InternalServerErrors due to NPE, the better!
  • Extension Functions - Kotlin allows you to add new functions to any type (e.g., to the String class). It's just syntactic sugar that replaces Java's static util functions, but it makes the code much more readable.
fun String.myOwnStringFunction() = this.toUpperCase() + " | Made with Extension Function"
fun main() {
print("HelloWorld!".myOwnStringFunction()) // OUTPUT: HELLOWORLD! | Made with Extension Function
}
  • Functional programming support - default immutability in collections, value objects, higher-order functions, useful helper functions in the standard library - FP support in Kotlin is so much better than in Java
  • Type inference - most often, you don't need to provide the type of the variable - the compiler is smart enough to know. One could say it's the same in Java since Java 10, but Kotlin took it to the next level - it can infer method return type or class property type, which is not the case in Java.
class Example {
fun functionReturningString() = "NO RETURN TYPE SPECIFIED"
}

The dark side

Moving all language specificities aside, the thing that disappoints me the most is IntelliJ's performance while working with Kotlin. Code completion, syntax highlighting, refactoring - I can always feel a slight delay while working with Kotlin. The problem is not existing while working with any other language (besides the performance, JetBrains IDE works excellent with the language).

Renaming method parameter in Kotlin (pay attention to how long the red frame around the variable name after renaming action lasts):

Kotlin

Renaming method parameter in Golang (renaming almost instant):

Golang

There are a few language design choices that some people dislike and/or find disappointing. Lack of possibility to extend data classes, lack of Java-like static methods & fields, classes that are final by default (which is inconvenient while using certain frameworks) - this kind of things are bugging some people. For me, these are not critical and certainly do not outweigh all the pros that come with using Kotlin on the server. However, you should be aware that Kotlin, just like any other tool, is not perfect.

Final thoughts

From developer experience point of view, Kotlin is a significant improvement, especially if you jump from 'old' Java (by old I mean 8, which is still very common in big enterprises). By using Kotlin, you end up writing less code. The code is more readable and safer - which is critical in the backend, server-side applications. The transition from Java to Kotlin is very smooth - you can become productive very quickly and develop your expertise with time. You don't need to learn new frameworks, as the same frameworks and libraries you have used before with Java do work with Kotlin as well. If you want to improve your server applications on the JVM - you should definitely give Kotlin a try.

Engineering