Kotlin’s first service in production
After some PoCs with the language, first made on a weekend in the presence of some beer, we confirmed that this was a fascinating language and deserved a serious test.
It was then that we decided to extract a small, well-defined context from our monolith to a Kotlin microservice. This context, which dealt with automated credit policies, was well decoupled from the rest of the monolith, and had been written in Ruby, using Hanami. We took care when extracting it: taking this context out of the monolith wasn’t the only goal, we also wanted to evaluate Kotlin and its frameworks. To do this, we decided to migrate the code as faithfully as possible to its Ruby implementation, keeping the same classes, method names, and attributes. Of course, we had to add a few more things, such as interfaces, due to the switch from a dynamically typed to a statically typed language, but on the other hand things we would’ve needed to add weren’t there as well.
Comparing Ruby and Kotlin
At the end of the rewriting of this context for Kotlin we made a presentation of the results for the entire company. We gave an overview of the languages and then showed examples of the code in Ruby and Kotlin, alongside some other metrics. Besides extremely high expressiveness and readability (one of the bases Ruby covers well), Kotlin showed a decrease in the amount of code generated. The context written in Ruby had exactly 1174 lines while the code in Kotlin, which performed the same function, had 650 lines. We noted that the languages are similar at an advanced level and neither of them have flagrantly different features, like for example the use of begin and end braces on method and class definitions, which could have artificially caused this row discrepancy. Though the row number metric is not the best for this kind of comparison, we observed the result perfectly reflected our perception of simplicity. We observed a decrease (or absence) of boilerplate for: creation of public properties in classes, constructors, immutability, handling the presence or absence of objects (null safety), etc. Again, with less boilerplate and with the compiler help, we also had fewer unit tests.
Regarding the implementation of repository patterns, we used Spring Data. We found it very easy to use and configure. There is very little boilerplate code, as well as extensive documentation and material. However, one feature we didn’t like was the need to use annotations in domain classes. One of our options to avoid annotations was to configure them via XML, but we found that even worse. An interesting alternative offered in the world of Kotlin is Exposed, to which we quickly encountered limitations when evaluating (as it is still very new), so we aborted. In the end, we agreed to stick with Spring Data and the white noise caused by its annotations, unfortunately still very common when using Java libs : ‘(.
Java or Kotlin Libs?
We have a preference for using Java libraries when they meet our requirements reasonably well (which happens often), for the reason of library maturity and documentation (beyond the point found below, which is the ease of contracting Java developers). However, when we find Kotlin-specific libraries that are far superior to those already offered in Java, we use them without inhibition. A great example is Arrow, a library inspired by ScalaCats, which implements a series of abstractions to address the functional paradigm, something we are exploring more and more in Kotlin (we will soon publish some posts about this).
The learning curve
During the migration process there was A LOT of new stuff to learn, and believe me when I say the language was the easiest of it. JetBrains’ own philosophy and strategy in creating the language follows this. Kotlin doesn’t bring anything revolutionary to the table, but it relies on the best features of the latest languages and those with a wide range of adoption and space within the enterprise market, such as Java and C#. Therefore, its syntax turns out to be very intuitive for most developers.
However, life is not a bed of roses. For developers who have never worked with anything from the JVM stack, the learning curve not only surrounds the language but the entire ecosystem as well. “Will we use maven or gradle to manage dependencies? What web framework will we use, Spring or Ktor? Which testing framework is better? What ORM to use?”. These questions and a few others constituted 80% of the learning curve. The language was about 20%.
Impacts and Results
In addition to the positive impacts in expressiveness, succinctness, and the advantages of mature frameworks, the language allowed for faster development and a better allotment of our time to focus on what we wanted. The business also had a lower error rate in things the language seeks to avoid, such as a null reference exception. The error rate with this specific bug, to date (and we already have many other services in Kotlin), is exactly 0. That’s right, zero null reference errors (in Kotlin, although is technically possible, for this kind of error to happen we have almost try to cause it).
In addition to the technical aspect, we have had other benefits that, although was not our main motivations, were always in the “pros and cons” listings of the languages analyzed. One such point is the “ease of hire” factor. Unlike Ruby, whose popularity is slowly waning, Kotlin is on a growth curve and has big companies backing it. We cannot yet know how long this growth will be sustained, but we are convinced that the language will gain more and more space in the market and that it will take a lot of Java’s market space in the coming years. It is exactly these developers that we are looking for. Finding Java developers is easier, and many of them have a keen interest in working with Kotlin. Hiring good Java developers and bringing them in to help us work in an ecosystem they already know, with Kotlin, has not been a difficult task.
One last point is the benefit of diversity. Creditas is a company that embraces diversity in everything aspect, from social to technological. Bringing in people from different developmental stacks made us reflect and question our assumptions and fosters the discussion of conflicting ideas, which has been very productive.
Open Source Contributions
During this period of constant learning, in addition to making contributions to frameworks such as Arrow, Kluent, and Fuel; we have also created some sample projects that are available in the following repositories: DDD Rails Sample and Kotlin DDD Sample. It is likely that in the future we will open some other projects that currently are just internal. Wait on it :)
The purpose of this series of posts is to share our history, paths taken, and fruits harvested. We do not want to suggest that Ruby should be abandoned, nor that languages that have not been chosen are inferior to Kotlin, or that languages not considered on our list are not worthy of merit, nor that we have studied absolutely everything we should in our PoCs and languages mentioned. Making bold decisions based on incomplete information and uncertainties is a part of life. That’s what we did. And we are very proud to reap the rewards of our choice with each passing day.
Our goal in Product Technology is to create the best solution to our customers’ needs by offering the most complete and innovative portfolio of financial products from financing to investing. We’re here to build a fully automated and intelligent digital platform and we’re looking for people who’ll join us on that: https://jobs.kenoby.com/careers-creditas