Learning Spring with Kotlin – Sprint 0
thatsabug.comI've been using spring and Kotlin together for nearly 3 years. Since then, a lot of Kotlin specifics have crept into Spring. Also, there's an effort to move to a fully declarative Kotlin DSL for driving spring (kofu), which gets rid of basically all anotations, AOP processing, and other magic that makes spring both nice and hard to debug. At this point, if you are using Spring and not using Kotlin: you are doing it wrong. You are missing out on a ton of stuff the Spring people did to support Kotlin. Yes Java is still supported but it is increasingly in the same way that Google hasn't completely deprecated Java for Android yet despite that obviously very much being a second class citizen in that ecosystem.
In short, if you are a conservative institution like a bank and evolving your tech stack at a glacial pace; yes you can still use Java. Everybody else, you probably should be using Kotlin.
My advice to anyone looking to do this is:
1) Do it, it's easy and you end up with better code. You can start simple and mix Java and Kotlin code. I would advice starting with some tests or some new features.
2) Beware of the java idioms and cruft in Spring and do things the Kotlin way. So, DSLs instead of annotations + AOP, co-routines instead of spring flux, immutable data classes instead of java beans, etc.
3) Spring has a lot of stuff and not all of it is great or even good. IMHO the added advantage of e.g. spring-cloud or spring-data is very limited and sometimes it's just very limiting/backwards. I ripped out spring-data on several projects where people naively supposed it was the easiest/best way to interact with Elasticsearch. It turns out it is neither. Spring cloud sounds nice until you realize most of the stuff you need is supported in the amazon driver but yet not exposed in spring-cloud. Also all the code samples on Amazon are for their own driver and not for spring-cloud.
4) Re-assess if you actually want to go anywhere near hibernate. IMHO it made a lot of sense when java beans were hard to write. Kotlin data classes take away most of that pain and there are some nice alternatives. Also, you should be using non blocking IO for talking to your DB and hibernate is still stuck on blocking IO. That in itself is a good reason to not use it on new projects.
Once you get a bit further, you'll find yourself wondering if ktor would work. The answer is probably yes. It's actually pretty good. Spring does a lot of things but ultimately do you really need all of that and the associated complexity?
Kotlin is taking over the JVM ecosystem like Scala could only have dreamed of doing. The JetBrains team took one of Scala's more glaring weaknesses, the tooling support, and made it the foundational strength of Kotlin. 100% inter-op with Java. It's a first class citizen alongside Java in IDEA for a long time, since before the language was even production ready. Supremely clean, coherent, and most of all concise documentation pages. They got support from Google surprisingly early on in the Android world. They have a really nice, web-based playground hosted on the main docs domain at play.kotlinlang.org. etc...
I think it's a good sign when you see it start to creep into random, seemingly disjointed areas, like how Kotlin DSLs are invading a lot of areas of dev-ops/infra-as-code. It speaks to the strengths of the language rather than the passion of the supporting community, imo.
Performance driven purists can continue to doubt, but I'm betting long by a mile on Kotlin, all it will take is time, more and more frameworks and ecosystems will integrate Kotlin, and rough edges can be polished.
>I would advice starting with some tests
The way that Kotlin extension functions and DSLs breathe fresh air into JUnit testing is hard to fully communicate. For me at least, it makes writing test code actually not suck anymore, and even fun in some cases.
> They got support from Google surprisingly early on in the Android world.
You can thank the Oracle lawsuit for this.
Google has been preparing Kotlin as their escape hatch.
So they better get that Kotlin/Native ready, and rewrite all of Maven Central libraries being used in Android in Kotlin/Native.
Kotlin does not really take over JVM ecosystem, very few projects use Kotlin outside of Android world (which is not really JVM ecosystem). And even for Android, I believe, its promotion from Google was more to mock Oracle.
> And even for Android, I believe, its promotion from Google was more to mock Oracle.
It certainly helps them shift away from Java reliance, but have you ever actually coded an Android app? Android is stuck on Java 8 syntax, and Kotlin fixes that. It has a laundry list of QoL improvements over Java (delegation, type alias, extension functions, data classes, better string formatting, operator overloading, smart casting, to name a few) that make it so much more pleasing to use over Java. ESPECIALLY when coupled with the aging Android framework, which it enhances greatly.
That is how Google plays the game to sell Kotlin, by placing it against a half baked implementation of Java 8.
If Android had an up to date full implementation of Java, the sales pitch wouldn't work that well.
In fact, there are several people that mistakenly think Android Java is Java and then get positively surprised when they learn about the real deal.
Kotlin is awesome language, don't get me wrong. I used it for some of my backend projects. I just don't see it overtaking Java ecosystem, it's a wishful thinking. Developers don't care about QoL improvements that much, they just follow vendor standards.
Another aspect is that JVM evolves without taking Kotlin in consideration, so there's always danger of JVM introducing a feature that won't be available in Kotlin (and JVM has several interesting developments namely Loom, Valhalla). Like it or not, Kotlin is second-class citizen on JVM. It made sense in Java 7 era, when Java development seemed stuck and Java thought to be mostly done. Times changed, Java is evolving at much faster rates (I would even say, faster than Kotlin these days).
This is about JVM, of course on Android Kotlin is first-class citizen and everything's good for Kotlin there.
These are all Android problems. Not Java's.
Kotlin is meaningless in the JVM ecosystem, it is just yet another Groovy, Scala, Clojure and it will never get beyond single digit percentage.
In about 5 years it will be as meanigfull as former guest languages are today.
Without having any language support for what Java 25 will be capable of doing with value types, GPGPU, SIMD, Graal integration, as per JVM/Java roadmap, as it will be jailed by what Android is able to do.
I'm of the opinion that Spring is 100% unnecessary and pretty much in all cases just straight up a bad choice. There's a ton of great companies that are built on it and depend on it, but man is using it unpleasant to say the least.
Admittedly I haven't looked at Spring w/ Kotlin at all, but I can't imagine it would solve fundamental annoyances I have with it (Why can't I capture logs globally while actually handling my exceptions locally?).
IMO the best library for Kotlin web stuff is http4k. Really nice documentation, first class integration for marshalling JSON with Moshi instead of Gson/Jackson, and very good, up-to-date consistent documentation.
I would love to use http4k, but last time I checked it lacked support for coroutines, which Ktor makes heavy use of.
Great comment, and I wanted to reinforce the fourth point. At this point, Hibernate is more of a liability than an asset. I once used it a couple years ago in a Spring Boot + Kotlin project and the hidden and hard to debug complexity it added made some problems that should have been simple to fix incredibly difficult.
Agreed on all points, also that spring-cloud is subpar. Also, if you're using Gradle (I usually prefer Maven), there is now a Kotlin DSL. Glad to get rid of Groovy.
The Kotlin DSL is an improvement but it still suffers from a lot of groovy legacy. If find it is almost impossible to navigate my way around the DSL using autocomplete. Absolutely everything is both different from groovy and somewhat counter intuitive.
Maven is a bit easier to Google; mainly because it stopped evolving in any meaningful way about a decade ago and nothing is really changing. The XML feels really backwards at this point It seems attempts to move away from that have stalled. It's also a lot slower to run. We're using it on the project I'm currently on and it eats up a lot of my time. Every time I do an mvn spring-boot:run, it goes off and compiles stuff. Even after it just did that and literally nothing changed. Gradle is much better at eliminating unnecessary work.
> It's also a lot slower to run.
Huh interesting. Its the other way around for me, Gradle usually being a lot slower. Also, IDE integrations (IntelliJ) is usually better with Maven due to its declarative nature.
Why do you prefer co-routines to reactor flux/mono? I have plenty of flux/mono experience and I enjoy the api so I didn't even consider coroutines.
It looks like there is a kotlinx-coroutines-reactor module, I'm not sure if that's what you are referring to.
Co-routines + flows are basically designed to address some problems with reactive things like flux or rxjava. Spring can support co-routines anywhere it currently supports flux/mono as well. Basically, the way things are going. I consider flux to be effectively deprecated. IMHO it will probably stick around for a long time to support those who depend on it already (like lots of other things in Spring) but you should avoid using it.
Basically, with co-routines you lose the need for all the container types like Mono, Flux, etc. and replace them with simple suspend functions. This makes code a lot more readable, easier to test, and reason about.
Instead of callbacks for errors, you can rely on the normal try/catch. Any uncaught exception causes the co routine and co routine scope to fail and cancel in a controlled way. The Flow API allows you to do reactive style processing. It also handles back pressure and a few other things.
Basically fully reactive code written using this is structurally very similar to the normal blocking version of the code.
I actually enjoy the container types and callback errors. The wrapper type Mono<T> acts like a monad of type Promise<Either<T | Error>>. This completely isolates a computation from the rest of the code and puts all of the error handling in one place.
I despise side effects as well as try/catch error handling so these are an especially welcome improvement to java/kotlin.
I don't have much experience with coroutines/flow so I can't actually compare the two approaches but this is my experience with the reactor library. Its unlikely that I will get a chance to continue using Kotlin in the near future but next time I do use Kotlin I will definitely try the coroutine approach and see how it compares.
I just wish that Kotlin had a testing solution as great as Spock (groovy), or rspec for ruby, for that matter.
But you mention not using Hibernate. I fully agree myself, and used JOOQ, but what is your preference?
Kotest is pretty nice as well. I haven't really tried spek but it sounds alright. I'm generally not that much into BDD; IMHO it just leads to lots of verbosity stating the obvious. Mostly junit5 with some nice assertions library (assertk) and mocking library (mockk) covers most of my needs.
For database stuff; I've always preferred good old JdbcTemplate in Spring combined with TransactionTemplate. IMHO writing mapping code is not that much of a problem; especially if you stop pretending that your complex object hierarcy maps 1 to 1 to tables and do a proper database design instead.
Writing that code sounds harder than it is. Compared to littering your models with annotations to say "this field is that column and this type", it's about the same lines of code. Except it's in one place where it is easy to maintain. Several frameworks exist to automate this business of course but given how trivially easy it is to do this manually, I don't see the value.
I just wish that Kotlin had a testing solution as great as Spock (groovy), or rspec for ruby, for that matter.
Have you tried using Spek?
I think I most miss data driven testing with everything but Spock. http://spockframework.org/spock/docs/1.3/data_driven_testing...
I'm a personal fan of Ebean. Its a lot more approachable and the author is easily accessible when you hit issues.
Do you have any thoughts about Groovy?
I still go with Groovy for scripting and tests with Spock.
Scripting is better because Kotlin doesn't have simple dependency management like Grape and compilation speed is way faster than Kotlin's. One of Kotlin's downsides is compilation speed and kts (kotlin script) is even slower. Also, Groovy still has ton of utility methods in GDK not matched in kotlin stdlib.
Other than that, I'm sad to say, Groovy is dead or in process of dying. I'm seeing companies rewriting Grails apps to Spring Boot and switching to Kotlin dsl in Gradle. It's a shame really, such a fun language to program in.
I feel like the sweet spot is using groovy where it makes sense, like parsing unstructured xml and json, reading in files, pojos, and of course spock. I was never the biggest fan of grails.
I’ll have to check out Kotlin though, seems like the new wave for sure when it comes to simple readable jvm code.
as much as I love Groovy, it is sadly essentially a dead language. None of the language bugs and issue I have with it will probably ever get fixed. Kotlin has learned a lot of things from Groovy, and carries on a similar tradition of "java with syntactic sugar", though they've taken it slightly farther. And with the support of both JetBrains and Google behind it, it actually gets the fixes and improvements it needs, combined with all the growing community and overall momentum.
If you're using Groovy you might as well use Grails[1]. It's built on top of Spring Boot but gives you Groovy interfaces to most of the things you'd care about.
With Kotlin out there, groovy is completely redundant. Also development seems to have stalled a little. I removed it from a few projects I inherited to simplify my life a couple of times.
What are you using for non-blocking database access? Is not JDBC a roadblock?
You run threadpool of JDBC workers and pretend like it's non-blocking now.
Though there are some experimental non-JDBC drivers using non-blocking I/O to move your blocking code from backend into database. In the end you're still blocking.
Why do you want db access to be non-blocking? Are you not interested in the result?
I want to resume business logic when the result is available, but I don't want tens of thousands of kernel threads blocked on results until java finally gets fibers. People who write synchronous drivers also tend to make big poorly-used connection pools which defeat TCP flow control.
> I don't want tens of thousands of kernel threads blocked on results
If that is because of the memory consumption of kernel threads, be aware you are trading less memory consumption for increased cognitive complexity (if you agree that asynchronicity is more complex than synchronicity).
True, if hardware cost nothing we might still be on python. Chaining futures with f1.thenApply(arg -> f2) hasn't been that bad, but kotlin's coroutine DSL does look better.
I've not yet done any non blocking DB stuff. JDBC is blocking but there's R2DBC in the spring world that you can use. There are probably a few other options.
Some guy writes an article where he says he is thinking about learning Spring with Kotlin and this is worthy of the front page of Hacker News??