The Holy War of Programming - Java vs Kotlin

Why We Stuck with Java Instead of Kotlin for Backend Development


Author: Artsiom Tsvirko Published on: August 26, 2023

Choosing the right tech stack for your startup is a critical decision. It shapes the future of your product, influences the talent you can attract, and impacts how well your technology can scale.

When we began our project, Java was dominating the backend landscape, while Kotlin was just gaining traction. Although Kotlin has gained popularity, we opted to stick with Java for several compelling reasons.

Why Java?

From the outset, we recognized that our project would appeal to a broad audience. We also understood that as our ambitions grew, we would need a tech stack that could scale efficiently. While it might be tempting to pick a tech stack focused on rapid prototyping, doing so would likely require extensive re-engineering as we expanded. A notable example of this is Twitter, which had to shift from Ruby to Java to accommodate its growth. We wanted to avoid a similar scenario, so we chose Java for its long-term viability.

As our team grew, aligning our tech choices with the hiring market became essential. With a vast pool of Java developers globally, we minimized recruitment challenges, allowing us to focus on building a robust engineering team.

Additionally, our development team already had extensive experience with Java and the JVM. Opting for a different technology would have been counterproductive unless there was a compelling reason. Java’s proven scalability and reliability as a backend platform allowed our engineers to focus on delivering innovative solutions rather than grappling with unfamiliar technology.

Java also boasts a vibrant open-source ecosystem. For nearly every challenge we encountered, there was a reliable open-source library available to facilitate our development process. This ecosystem provided strong reasons for choosing Java as our foundation.

Why not the other JVM Language?

It’s important to differentiate between the Java platform and the Java language. While it’s possible to adopt the Java platform and write in another JVM language, we considered whether to stick with the Java language or explore alternatives.

At the time, Scala was the primary alternative to Java. We asked ourselves some crucial questions:

  • Productivity: Is there evidence that developers would be more productive with an alternative language? In larger contexts, evidence of productivity differences among languages is sparse, often reduced to personal preferences.

  • Features: Are there unique features or libraries in alternative languages that could enhance our product? This can be a valid reason to switch, but we needed to assess the maturity and future-proofing of any alternative compared to mainstream Java.

  • Expertise: Does our team possess sufficient expertise in the alternative language? Adopting a new language without adequate knowledge can lead to non-idiomatic code, creating technical debt from the start, especially in a rapidly growing team.

  • Developer Happiness: While subjective, the overall happiness of developers is essential. However, can we scale our team quickly enough after adopting a niche language? Every hour of engineering productivity counts for a startup.

None of these considerations were substantial enough to lead us away from Java. In retrospect, while other JVM languages gained momentum, we are content with our decision to stick with Java.

Adopting Java

Choosing a tech stack is just the beginning; maximizing its adoption is another challenge. Early on, we established a dedicated Java platform team focused on building our Java developer platform. This commitment enabled faster product development as we scaled.

Key achievements of our Java platform team include:

  • Developing internal libraries built on Spring (Boot) to address common challenges faced by product teams.
  • Regularly upgrading to new Java versions: all services now run on Java 17, with plans to transition to the next Long Term Support version as it becomes available.
  • Providing a Gradle-based build infrastructure that simplifies build processes for teams and ensures dependencies are always up to date.
  • Implementing static code analysis with SonarQube, allowing us to maintain high code quality and facilitate large-scale code improvements.

We heavily invest in our internal Java development platform, leading to a modern, cohesive approach to Java development that enhances developer satisfaction and effectiveness.

Enter Kotlin

Today, Kotlin is gaining traction as a prominent JVM language. Should we consider switching to Kotlin? While we’ve made strides in that direction, it has primarily been for our mobile development, where Kotlin has become a natural fit.

For our Java backend, the situation differs. Although many view Kotlin as a superior version of Java, we find Java to be a robust language in its own right. Moreover, Java has made significant advancements in recent releases. Recent updates to Java include:

  • Records (Java 17): Similar to Kotlin’s data classes.
  • Pattern matching for instanceof (Java 16): Offers functionality akin to Kotlin’s smart cast.
  • Sealed interfaces (Java 17): Features also found in Kotlin.
  • Text Blocks (Java 15): Facilitate multi-line strings in Java, with upcoming String interpolation that exceeds Kotlin’s capabilities.

This evolution demonstrates that Java is effectively adopting many best practices from Kotlin and other languages, making it increasingly appealing with each release.

Switching languages is a major investment that requires reworking our tooling, retraining developers, and reestablishing best practices. Given the diminishing returns of such a switch and observing the rise and fall of earlier alternative JVM languages like Groovy and Scala, this investment does not seem justified.

Are We Having A Fear Of Missing Out?

While we admire some features of Kotlin, we believe that Java is closing the gap. Kotlin is a more expressive language with a richer standard library, adhering to the ‘batteries included’ philosophy. Its collections and streams APIs are more extensive than Java’s counterparts, and features like extension methods, operator overloading, and function literals with receivers enable the creation of DSL-like libraries. However, increased expressivity can lead to ambiguity in what constitutes idiomatic code, raising the cognitive load during development and code reviews. In contrast, Java benefits from decades of community guidelines and established best practices, often supported by tools like Error Prone and Checkstyle.

Kotlin’s built-in null-safety is often cited as a significant advantage. However, we can achieve similar outcomes in Java by using @Nullable annotations combined with static analysis tools. Kotlin’s coroutines (with async/await) are fantastic for building high-throughput, non-blocking services. In Java, we use reactive programming libraries, which can be cumbersome. Yet, with the upcoming Project Loom, Java promises to allow developers to write sequential, seemingly blocking code that executes asynchronously on Virtual Threads. This shift represents a compelling alternative to Kotlin’s coroutines, as it necessitates broader changes to the Java platform rather than just the language. While Kotlin’s approach is innovative, it may not fully align with Loom’s platform updates, showcasing the complexities of being a first mover in a dynamic ecosystem.

Kotlin’s innovations contribute positively to the JVM ecosystem, and we appreciate seeing some of the best ideas return to Java.

Making Your Own Choice

Ultimately, the right tech stack is a context-dependent journey. There are no definitive answers—only various angles to consider and questions to explore. Whatever you choose, commit to your decision by investing in tooling and enhancing developer experience. This investment boosts productivity and team satisfaction while grounding your commitment; any future switch must deliver genuine benefits.

Our dedication to Java stems from our extensive experience with it and our belief in its ongoing evolution. As a scaling company, we reap the advantages of Java’s blend of maturity and innovation every day!