We're four years into a journey that shouldn't have worked but did. I have a lot of experience with mostly server side JVM stuff and a little bit of frontend. Before 2020 that was mostly standard stuff including a largish javascript /typescript project and a bit of react.

In 2020 I took over the skeleton team of a startup. My main (junior) developer had a bit of Android and Kotlin experience; not a lot. We had no resources to get more developers. I was focusing on building the backend, which barely existed at that time so it was all new code. I picked spring boot and Kotlin for that.

We needed web and IOS apps in addition to Android and the Android app (the only thing that had been built) was a bit of a mess and honestly there wasn't much worth keeping. And with just one developer it was clear the replacement was going to be a web app. The obvious thing would have been to potty train my junior developer on react/typescript and hope that after a few months he would become a bit productive.

Instead I took a wild bet on kotlin-js. I honestly did not expect that would work. But it did. We did a brief research spike. Very successful. My junior developer did all the work. And we continued from there. I always had the plan to at some point just parachute in more developers and re-do it properly. But that never happened and nor is it needed.

Fast forward a few years, we still work with kotlin-js. Over the last four years that became more stable and better supported. It's great. It's a four year old UI code base and we're making lots of changes with confidence all the time. Kind of the gold standard for a good code base. It sure has its issues but it's under control.

Mostly I can't tell the difference whether I'm working on server or frontend code. It's all Kotlin. Kotlin multiplatform ensures we can reuse a lot of code on both. So the default place for code to go is in some multiplatform library. Which then ends up being used on both server and client.

We use a small, obscure UI framework for kotlin-js called fritz2. It emulates a lot of what react does but in a Kotlin friendly way (strongly typed, co-routines). We use tailwind for styling (after some adventures with other stuff) and are slowly converging on maybe adding daisyui to that mix (which is nice).

A lot of stuff in browsers is of course asynchronous and as it turns out, Kotlin's co-routines are awesome for that stuff. We have a lot of long running or recurring stuff happening in background co-routines. Handlers are suspend functions, etc. Integrating existing javascript frameworks is fairly straightforward. We have a few of those. Things like maplibre and a few other things.

The point here is that frontend code doesn't have to be Javascript and it doesn't have to be miserable like many Javascript projects become. Where you run your code and what language you pick for that are two independent choices. And if you have proper well designed code, it should be testable. Just because it runs in a browser is no good reason for that to stop being true.

I wouldn't recommend my choices four years ago. But at this point browsers run a lot more than just Javascript. Getting stuck with that stuff is a choice, not a necessity.