Cudling with Kotlin
In the Margo Bank development team, we like to take a collegial and rational approach to all important technical decisions. We also make a point of documenting these decisions, which forces us to be rigorous and objective rather than (excessively) opinionated. Over time, this has created a decision documentation trail that is a real asset. It not only allows new developers to understand where we are, but it is also an incredibly valuable resource when new facts compel us to look at previous decisions in a fresh light.
Selecting a programming language is definitely one of these major decisions, and all developers are fully aware of how much this choice can affect their productivity and joy at work.
A little while back, Jérémie wrote a blog post explaining our decision to adopt Elixir for the development of our Core Banking System. In this new post, I’ll explain why we decided to add Kotlin to our toolbox.
The transactional core of Margo Bank is now humming in Elixir. Hindsight has confirmed that this was a great choice for the job. The transactional core’s mission is to maintain Margo Bank customer account records with the highest possible availability, reliability, and auditability. This is the type of task in which Elixir’s actor model combined with an Event Sourcing architecture really shines.
That said, as we started developing new services beyond the core, we came to the conclusion that Elixir wasn’t necessarily the best choice for all developments. Why is that? Because not all developments have the same productivity trade-offs.
The transactional core is a self-contained component. Its data structures are few and very stable, and it isn’t highly dependent on other services. On the other hand, services like the back-end of UI applications have many dependencies on other distributed services, manipulate a large variety of data types, and have a large and rapidly evolving REST API. For this kind of service, Elixir didn’t appear to be the best choice, because:
- when writing distributed services with a lot of data access and integrations with third-party components, developers have come to expect a large choice of mature and feature-rich third-party frameworks, libraries and tools, with a large community of developers providing support through examples and articles. Elixir’s high quality but small ecosystem doesn’t provide these. For the transactional core, this limitation is outweighed by Elixir’s inherent support for high availability. For other kinds of services, however, the productivity trade-off is different, and the lack of frameworks, data utilities and third-party API wrappers can sometimes be a handicap.
- Elixir’s dynamic typing, which can increase productivity for a small team working on a self-contained component, actually makes it harder to write reliable code when there are multiple dependencies on internal or third-party services and a larger number of developers involved. Dynamic typing lacks the great affordance that static typing provides to libraries and frameworks. It also limits the assistance IDEs can provide for code autocompletion, cross-module refactoring, and static bug detection.
Therefore, we decided that we needed to look for an additional language, more adapted to this kind of development.
Looking for a backend development language
The search for a statically typed language with a large ecosystem naturally narrowed our choices down to the three major statically typed languages running on the Java VM, which has one of the largest and most mature ecosystems available — arguably the largest when it comes to backend development:
- Java itself
Java used to be incredibly cool. The first JavaOne conference back in 1996 was like “mai 68” for the nerds (replace with “Woodstock festival” if you don’t know French culture). Java brought a lot of highly productive features like garbage collection, reflection, portability, a rich offering of standard libraries, at a time where C++ was getting bogged down with an overly slow evolution process. Unfortunately, Java is now the incumbent, and it is becoming increasingly slow in adopting new productive features.
Scala was a serious candidate; however, its legendarily slow compilation time alone was enough to steer us away from it. We also found that Scala’s approach to language design sometimes lacked pragmatism and created too much complexity.
The reasons that compelled us to choose Kotlin are:
- Seamless bi-directional interoperability with Java. In theory, Kotlin code can use any Java library or framework as if written in Kotlin, without any helpers or wrappers, and vice versa. In practice, this is 95% true — sometimes Kotlin or Java needs a little help.
- Conciseness. Many common design patterns are captured in Kotlin as native language constructs, for instance data classes or class delegation. This makes code very concise, which is great since it’s generally agreed that productivity is closely linked to the amount of code that fits in one screen. JetBrains claims that Kotlin code is 40% more concise than Java code, and our experience supports this.
- Type safety. Kotlin has several improvements over Java’s, like the absence of raw types, the generalisation of read-only data structures in the library, and of course the awesome Null Safety.
- Great IDE support. Compilation is very fast, and IntelliJ (unsurprisingly) offers a great support for autocompletion, refactoring, static error detection, and automatic tips for code improvement. One of the team’s favourite features: in IntelliJ, when you paste Java code into a Kotlin file, it is automatically translated into Kotlin!
- Fast and pleasing learning curve for Java developers. Moving from Java to Kotlin is not only fast, it’s also enjoyable. During the period where the team was learning Kotlin, working hours were punctuated with exclamations of satisfaction, and developers were happily sharing their latest discoveries about the language.
- A growing popularity, which ensures its perennity and attractiveness. Google officially supports Kotlin on Android, and Kotlin is rapidly gaining traction for backend development. Stack Overflow’s 2018 developer survey reports that Kotlin is loved by 75% of developers, second only to Rust (we might talk about Rust in a future blog, stay tuned).
On a more subjective note, we really liked that Kotlin seems designed with a purely pragmatic approach. Every Kotlin feature feels like it’s here to address an actual developer pain point, rather than to strike some theoretical or academic chord.
The choice of Kotlin was not unanimous in the team — technical choices of that scale rarely are. But once the decision was made, everyone was supportive. In the end, we’re all happy with our choice and are not looking back. So far, we haven’t found any real drawback to this decision.
- engineeringBenoît de La Forest6 min read
- productJulien Bleton6 min read