From zero to 10M lines of Kotlin
engineering.fb.com
engineering.fb.com
Whenever I try to get into another language today, the lack of language-aware editing tools are my main source of frustration - specifically, I expect navigation, refactoring and completion to work flawlessly. I know of no other programming language that puts as much effort into context- and structure-aware refactoring - if another language does one day replace Kotlin, it will need to offer a comparable editing experience.
I similarly get irritated using anything else now.
Kotlin's type system is nominative and so while it is not as flexible as typescript (no intersection types, conditional types etc.) it also means that you don't run into those multi-page long type errors which need 5 mins of debugging to figure that some deeply nested object is null where undefined is expected.
It is particularly funny when the language server truncates the errors and it becomes impossible to infer the actual issue from the message. Every now and then I find myself extracting things out of objects and adding type annotations to simplify the errors. It is doable, but never needed in Kotlin.
[1]: https://github.com/microsoft/TypeScript/issues/14833
[2]: https://io.livecode.ch/learn/namin/unsound
[3]: https://arxiv.org/abs/1605.05274
No language is perfect. Your comment is absurd, and the citations are pedantic.
Variance rarely comes up in Kotlin because they don’t push immutability as much as Scala did. It’s like, there as an option but I’ve used it a total of two times in the last two years of full time Kotlin programming. Compared to typescript, Kotlin is fairly boring, but it is tooled well and is a vast improvement over Java.
A big part of why Kotlin works is because IDE support is so fantastic now. And likewise, a lot of the reasons Java ties developers' hands is because there was no guarantee about the IDE they would be using.
As a trivial example, declaring variables. In Java, you have `final String foo = "bar";` while in Kotlin you can just say `val foo = "bar"`. They type is implied, but if you aren't sure, you can hover over `foo` and the IDE will tell you what the type is, so there's no longer a reason to type it out every time.
Also, you can now have multiple classes in a single file. When Java was created, the one-class-per-file rule made finding the source code to a file easy; just do a `find` for `Classname.java`. But with a modern IDE, you can just command-click an instance and be taken to the source code automatically.
Java's boilerplate is meant to make code discoverable, but the Kotlin IDE does that work for you, allowing developers to be more flexible.
var foo = “bar”;Java is way too verbose to be workable if I have to write out all of the class declarations and anonymous classes and “equals/hashCode/toString” boilerplate and “ExtremelyLongClassName foo = bar.extremelyLongMethodName(…)”; and there are too many pitfalls like comparing with “==“ instead of “Object.equals” and implicit null. Except that I don’t have to write out anything or worry about any pitfalls (well, implicit null still hits me sometimes, but @NotNull and @Nullable make it much less common) in IntelliJ. IntelliJ has such powerful and seamless analysis, it does aggressive code folding on Java 7 so that you are looking at code with “var”s and lambdas. It also has built-in support for popular libraries like Spring and Lombok.
IntelliJ single-handedly turns Java from a verbose, legacy language into something I could actually recommend and start new projects on (although at that point I usually go with Kotlin). And Eclipse, NetBeans have very deep Java analysis as well. Java may have started off without expecting IDE support but now IDE support is responsible for a large part of its popularity.
If I cannot access documentation for a library/function at a mere tap of a button - that language isn't going into my toolbelt. I have to code in like 5 languages at my dayjob, that's at least 3 different types of semantics for ==.
Java, and Kotlin, are great at that. It's the major pitfall for Scala(and a laundry list of others), on the other side(hey implicits and converters, that make even IntelliJ go "wut?"). Those long names in Java are actually a godsend. Give me "int indexInTheArray" over "int x" anyday. Explicitness of Python is also a pleasant thing to work with.
People forget that most software is boring website backends, business processes and similar tools. Most devs don't have mental capacity to devote to "the one and only" language+platform+ISA.
It might also be an unhappy coincidence, but the library I was playing around with (scrapy) also had HORRENDOUSLY documented code. With Java/Kotlin, you can just click into any method/class and you'll get a useful doc string 99% of the time. With Python, I had to read the (incomplete) documentation.
(But I do agree actually, a good IDE is much better investment than a minimal syntax revision)
Java is a surprisingly expressive language even though it does have a few warts.
I do think there should be a distinction on what is lumped as verbosity. Golang is vertically verbose with its error checking, and Java in my experience is at least more horizontally verbose (long lines).
So long as everyone on your team uses IntelliJ, then no problems. But not everyone uses IntelliJ, and now you've written code that is only easy to parse in a particular IDE...
For this reason, and being able to read my own code outside of just my IDE (say, in Github, etc), I've found it more necessary to be explicit with my typing in Kotlin for all but the most trivial snippets.
Besides, reading implicit types has _never_ been an issue on any team or collaboration effort in the 30 years of my programming experience and I find it _really_ hard to believe anyone that claims it is a bonified problem for readability. In fact the complete opposite is true for me, personally, that too much information in front of me becomes noise and the signal gets lost.
This is something I've learned about Groovy that I really like. You can declare variables in multiple ways (terms are my own):
foo = 1 # normal assignment
var foo = 1 # declared assignment
int foo = 1 # typed assignment
A lot of our current code used the first "normal assignment" that you would see in shell scripts, Python, whatever. There's nothing interesting about this.
I started using the second kind consistently. The "declared assignment" says "I am creating a new variable foo and its value is 1". If you are not creating a new variable (i.e. if the variable exists already) your program fails. This is a great way to ensure you're not overriding variables from earlier in the program or from another scope. My experience tends to be that this throws errors "now" for mistakes you're currently making (at this point in the control flow) by overwriting a variable that existed before.
The last is a "typed assignment", where you're not just saying "I am creating a new variable", but specifically the type of variable is important. This errors if you ever try to misuse a variable down the road, and helps with ambiguity. It solves the "somestring = somestring.split()" issue of discounting a variable as the potential issue due to "it can't be this one, that's a string" being right for a while and then wrong for a while (or vice-versa).
Anyway, all that to say: not having to specify data types is nice in some circumstances, but I've been leaning into adding data types to my code just to make absolutely sure I know what everything is all the time. Not having to in Kotlin sounds nice, but I hope there's a way to be explicit about everything.
var foo = "bar"; // you can add final if you feel like it
You could always have multiple classes in a Java file as long as there was only one public class.
People still think of Java as stuffed full of wordiness, slow startup, garbage collection seizing up code, and long compile times, but that’s all in the past.
Please correct me if I'm wrong, but I don't think Java's dependency discovery mechanism has evolved much (if at all) from prehistoric times (hence, the slow startup remains). Although GraalVM sort of addresses that.
I can’t recommend jetbrains rust support enough.
Same, but this was the case for C# in Visual Studio 20 years ago.
> I know of no other programming language that puts as much effort into context- and structure-aware refactoring
TypeScript + VSCode do all this and more.
This isn’t to say that Kotlin isn’t absolutely incredible, but I do not want to undersell the pleasure that C# is to work in with its tooling.
As a huge fan of Jetbrains products (I maintain a personal IDEA Ultimate subscription) this was still a huge turn off to me.
> I know of no other programming language that puts as much effort into context- and structure-aware refactoring
Do you mean JVM language? TypeScript, C#, and F# at least come to mind.
> Do you mean JVM language? TypeScript, C#, and F# at least come to mind.
As I mentioned in another comment [1] on this thread, soundness and decidability are non-negotiable for me. Types in C#, F# and TS are all Turing Complete [2], and therefor these languages are tooling-adverse in the sense that static analysis is fundamentally unreliable.Ideally, you want type inference to terminate in the amount of time it takes to type a few keystrokes to get realtime assistance, so decidability is still an over-approximation from a tooling standpoint and in practice anything strongly super-linear is not a fun experience to use.
> I know of no other programming language that puts as much effort into context- and structure-aware refactoring
To soundness and decidability.
Was there some study done where soundness and decability of Kotlin is provided?
> I still don't follow...
The premise was "toolability" in language design, which is one of Kotlin's main design principles. To do type-safe completion, navigation or refactoring, you need soundness, and to do it rapidly in the context of a live programming environment, you need to be able to resolve a type with keystroke latency. Rapzid asked [paraphrasing] "why not TypeScript, C#, or F#?" to which I responded that I do not consider these languages "toolable" because type checking is essentially broken (i.e., it might take forever, or give the wrong answer). > Was there some study done...?
No, not that I am aware of. If you find one, I would be keen to read about it.I don't understand how you can come to the conclusion that type checking is broken and the language is not toolable? Can you give an example of where Kotlin's tooling achieves something that is not achievable at the moment in a C# IDE?
(I've not used Kotlin much but I have never felt that the tooling for C# was lacking, so I am genuinely curious)
> Have you ever used VS/Resharper/Rider with C#?
Yes, and to be clear I have never encountered these issues on practical problems. I am also not claiming Kotlin has or lacks these properties, just pointing out the fact if you care about toolability, soundness and decidability are important factors to consider and if there is a language design issue, it makes me less confident in any tooling which is built around it.It comes down to whether you want type checking to work all the time, or most of the time? If your answer is "most of the time", then just use a dynamically typed language with GPT-based autocompletion. Most of the time completion and refactoring will work just fine.
Re: type checking in C#, maybe try the following example?
https://learn.microsoft.com/en-us/archive/blogs/ericlippert/...
This is bad advice. Such an approach will go wrong often enough to burn you, and it will go wrong silently, whereas the kind of issues you describe tend to lead to something far more visible (and therefore correctible) like a complier internal error or an IDE refusing to perform a refactor.
a) "most of the time" being 60% (very roughly what I'd expect when doing renaming beyond a single function in JS, and that's being optimistic). b) "most of the time" being 99% (very roughly what I'd expect in a typical C#/java/kotlin codebase that isn't using too much reflection).
Even aside from that, I'd argue that nullability, array covariance, and reflection all have MUCH bigger practical impacts on typechecking reliability than Turing-completeness. Most languages will simply fail to compile if the computed types require too much recursion, making the issue relatively moot.
Type-checkers don't actually need to solve the halting problem to be sound, they just need to guarantee completion for _most_ types of code, and anything that can't be guaranteed can simply be rejected.
> "most of the time" being 60%
I would expect this number to get closer to 99% with LLM-based AI code completion tools, so the gap between dynamically-typed languages and "good enough" languages that claim to be statically typed (but are in fact unsound) will become much less significant. But if you want correctness, you will need stronger guarantees. > I'd argue that nullability, array covariance, and reflection...
I would mostly agree with that statement, with the caveat that it depends on what you want from your type system. Personally, I want the checker to be (1) sound and (2) decidable and (3) fast. For everything else there's runtime type checking. > Type-checkers don't actually need to solve the halting problem to be sound, they just need to guarantee completion for _most_ types of code
Although I don't see much difference between undecidable type systems and dynamically typed languages, as I wrote, I see undecidability as less of an issue. My main concern is unsoundness, which is more critical and unclear how to fix. By "soundness" I specifically mean the formal property that "well-typed programs cannot go wrong". "Most types of code" is just a statistical claim, in which case you might as well use Python or JS/TS with a linter. Too many statically-typed languages decide to go off and roll their own types without reading PL literature, then end up with dealing with these issues later down the line.How on earth do you consider Kotlin acceptable then? Kotlin's type system has never been shown to be sound or decidable and is visibly a lot more ad-hoc than any of that list (and if by some miracle it was sound or decidable last week, I'm sure whatever new feature they added this week broke it). I suspect the only reason it hasn't been formally proven to be Turing Complete is that Kotlin's developers haven't bothered to write a specification for it - as far as I can see your link's example for C# would translate directly into Kotlin with the same properties.
> link's example for C# would translate directly into Kotlin
T() is not possible for a generic T in Kotlin.These languages were stable, well-specified, and heavily studied for years before these cases were found. One person noodling around for a few weeks failing to find an example is not evidence of anything; a language being too obscure to have seen much academic study is not an indicator that it's likely to have a good type system, if anything it's the opposite.
> T() is not possible for a generic T in Kotlin.
T isn't generic in that code, it's just a class.
> One person noodling around for a few weeks
Well, I've been trying for about three years, but to be fair I'm a pretty slow programmer so you may have better luck. > a language being too obscure to have seen much academic study
Doesn't seem too obscure to me. https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=kotl... > T isn't generic in that code, it's just a class.
I think it needs to be a generic type in Kotlin for this to work, because otherwise it will dispatch to a single method. It's tricky to get Kotlin to do much compile time computation. Not saying that it's impossible, but LMK when you've actually tried it. Here's some sample code if you want to try encoding a Boolean logic:https://github.com/breandan/galoisenne/blob/2e465e7a753f6341...
Well, your own link proves it, doesn't it? Kotlin has nominal subtyping, contravariance, and generics, therefore its type system is undecidable.
> I think it needs to be a generic type in Kotlin for this to work, because otherwise it will dispatch to a single method.
That's the whole point - the type system has to decide which overload to dispatch it to at compile time, which it can only do by performing an arbitrarily complex computation. I ported that code directly to Kotlin and it doesn't compile but it "should", which, well, I don't know what you were expecting or what you'd accept as proof when we've already got an academic paper that proves the point.
> Well, your own link proves it, doesn't it?
That code simulates a bounded cellular automaton, which effectively reduces to a regular language, but that's about as far as I've been able to get. If it's possible to implement general recursion, I'm not quite sure how - yet. > Kotlin has nominal subtyping, contravariance, and generics, therefore its type system is undecidable.
They're not using Kennedy and Pierce's system, but it's closely related. See Tate (2013) [1], in particular, "In general, since declaration-site variance can easily be encoded into use-site variance..." > I ported that code directly to Kotlin
Can you share your translation of Eric's code? Maybe we can get it to work.Well, Kotlin has both declaration-site and use-site variance. So surely that result applies.
> Can you share your translation of Eric's code? Maybe we can get it to work.
Ah I didn't mean that code, I meant the earlier C# one from SO. https://pl.kotl.in/4BrbZAmZG
https://youtrack.jetbrains.com/issue/KTIJ-12496/Provide-inte...
https://youtrack.jetbrains.com/issue/KTIJ-10606/Support-movi...
https://youtrack.jetbrains.com/issue/KTIJ-5063/Extract-prope...
Kotlin as a language looks really cool, but I don't want to give up terminal-based, modal editing (which I'd have to in order to use IntelliJ).
Along those lines, I once tried diving into using Gradle outside IntelliJ, and I couldn't find any good resources to help with that. If folks have hints/links-to-blog-posts with regards to that as well, that'd be great!
[0] https://github.com/fwcd/kotlin-language-server#this-reposito...
I keep reading on HN and elsewhere that Scala's feature-full-ness encourages complex/unmaintainable code, but I gotta check out myself.
But Scala has a steep learning curve for the advanced techniques and the compile times can be very annoying when using those. And if you don't use the advanced techniques, you might as well pick Kotlin or Java instead.
* A conversion in 1 commit: complicating/breaking the 'follow' behaviour of 'git blame'
* A conversion in 2 commits: the first 'rename' commit doesn't compile, complicating/breaking 'git bisect'
I hacked up a script which makes the 'rename' commit compile (when I was working with a ~100KLOC Kotlin conversion). This may be useful for others:
* https://github.com/ankidroid/Anki-Android/blob/937a6560913ec...
* https://github.com/ankidroid/Anki-Android/blob/937a6560913ec...
This post is the (pretty short) summary of a lot of work which took us a long time to do. So it’s nice to get any kind of validation.
Personally I read HN a lot and it’s my main source for interesting articles nowadays. So that makes me appreciate HN upvotes even more.
2) If not, how many LOC is the largest Kotlin repo?
3) How do you tackle challenges of large codebases like language servers/synthax highlighting crawling to a halt in IDEs?
3) There's a lot of different things we do to work around such issues. Some things that come to mind right now: - We have plugins for Android Studio to avoid loading all the code at once that help. - We had forks for the Kotlin plugin for Android Studio to deal with some issues that were worse for our repo. (especially around module loading) - We also worked with JetBrains by pushing some fixes and they fixed a lot of the issues over time. - We do a lot of work as async jobs that run on the repo without you waiting for them, so you don't have to wait for such tools.
Probably 1000 engineers working on an app with each writing 1000 lines of code. I am not surprised Facebook app has >1 million lines of code. The app is bloated with features that it's user unfriendly.
- There's a few apps here. Plus a bunch of tools.
- The Facebook app is huge in terms of features. Just look at the menu with more things. I use very few of them, yet they're all pretty popular and justify themselves.
- Instagram is smaller than Facebook, but still has a lot in it.
- A lot of our code was optimized over time in many ways that add a lot of edge cases: internationalization, accessibility, optimizations for dealing with media, loading and data. It's can be easy to write something with much less code that looks pretty good at first sight, but all those extras really make the experience better and pay off for users.
- We build new features all the time, some code may be unreleased, some is in A/B testing and so you can have two or more pieces of code that do the same.
- A lot of test code.
- Not that much dead code. Trust me. I love deleting code! And I will happily spend time removing bloat. There's definitely a lot of dead code to remove, but it won't change those top line numbers by much.
(Also, it's 10M Kotlin lines of code, we have much more)
(I would love to know how many lines of code the other really big companies with big mobile apps have for comparison)
Where did this come from?
> As part of this step, we also apply our autocorrecting linters and apply various Android Studio suggestions in headless mode.
I'll check again with the people who built it and see if it's doable to open source it.
Is that list of disadvantages really accurate? Reading them, I get the impression that the "popularity gap" between Kotlin and Java was a major reason FB was behind the industry on converting to Kotlin. (For context for non-Android engineers reading this, Kotlin has been the first party recommended language for 3 years now, and has been supported for 5 years) I can't imagine y'all were interviewing Android candidates in Java? And while certainly the Kotlin ecosystem as a whole is smaller than the Java ecosystem, the Java Android ecosystem is miniscule.
My recollection from my time in the building at IG was that build times and binary sizes were the two heaviest lifts, almost to the exclusion of anything else. Am I misremembering, or did the conversation change? Or does that list of tradeoffs reflect a realization by your team that you'd need to convert everything to Kotlin, not just the Android code?
I think this is a good depiction of our worries. Our biggest is and always was build times.
> I can't imagine y'all were interviewing Android candidates in Java?
We let people choose their preferred language for a while now. Also, while this blog is celebrating some milestones in the conversion, some smaller apps and new code was using Kotlin for a while now.
> My recollection from my time in the building at IG was that build times and binary sizes were the two heaviest lifts, almost to the exclusion of anything else. Am I misremembering, or did the conversation change? Or does that list of tradeoffs reflect a realization by your team that you'd need to convert everything to Kotlin, not just the Android code?
Binary size has generally not been an issue. Build times are an issue. We migrated and migrating some optimizations we have to alleviate that. We're also crossing fingers for more wins from the new Kotlin compiler JetBrains is working on.
Other people are also working on Obj-J/Swift, but from my superficial knowledge I think it's a much harder migration to do. I really think the Kotlin team did amazing language and tool design which makes Java to Kotlin migration easier than almost any other language migration I could think off. Kudos to them.
With only Java is was <2mins and we're touching 10mins now.
I'm not sure what exactly the problem is as we still have to investigate. Our use of Kotlinx.html (which we absolutely LOVE as HTML templating tech) seems to be a part of the problem, but we have not gone to the bottom of it yet.
Nothing against the language, everyone on the team loves it (when Java is what we consider normal). The extra type safety we get out-of-the-box or have put some effort into (using KFunction all over the place) is really paying off.
IMO, it produces the most (subjectively) readable code, and is the only one that doesn't leave room for you to inject personal style in certain places. (ktlint will let you get away with one of several ways of formatting code, so long as it falls within some guidelines, which can lead to inconsistent formatting).
It's a superior formatting tool IMO.
Sometimes it'd hang for several minutes, memory usage would spike from time to time.
Didn't have time to bother debugging when ktlint just worked immediately.
I might give it another try for some personal projects since I did like the anti-bikeshedding angle with respect to configuration, but yeah linting can be a hard sell once build performance starts to suffer for it
Most sane code formatters & code standards: "don't use import wild cards!" Intellij's default since forever: "wildcards all over the place; please jump through hoops to get rid of them".
I've given up on having code formatters in my kotlin builds for this reason. Just not worth the hassle of trying to make both sides just do the same thing by default so I don't have to think about it. And then having to explain to every new team member how they have to customize their intellij setup so it isn't broken. I just gave up on it. These days I tell people, auto format your code, leave the settings alone. Try to only format the code you modified to avoid endless formatting related diffs.
The fix would be for Jetbrains to finally fix their IDE to have proper formatting that includes the behavior of organize imports and is perfectly in sync with whatever your build file says is the code style rather than whatever manual or built in stuff. Any IDE settings for this should IMHO just be removed as they are completely and utterly wrong in the presence of a build file that says how things should be formatted. Just default to whatever your build file says is the code standard with some sane default that just comes with the kotlin compiler. This should not be optional. You want to override it: do it in your build file.
Auto format on save in intellij. CI build verifies that nothing is mis-formatted because the IDE did it right because it does whatever the build file specifies needs doing without second guessing that. Have a githook to format before commit (optional). Zero manual fixing of anything ever related to formatting. It should not have a chance to go wrong and break the build unless somebody forgot to format or have a commit hook to do so. Just import the project and everything does the right thing. That would be the goal. IMHO it's not technically hard but it would require addressing what is likely to be quite a bit of technical debt related to this in intellij. And it's about time they did that.
Pretty sure the FB equivalent also has a matching .editorconfig
Also you could just setup a prepush hook so they run before your CI does which afaik both give you examples of.
There's a ktfmt IntelliJ plugin that does work most of the time IME, if you're willing to give it a shot.
The only time the pre-push hook fails is if a line is too long and there's no "canonical" way to fix it (so it'll create a method chain for example, but it won't do string concatenation since there's N ways to break a string across lines)
Yes, it's a minor additional step at first, but it's like 15 minutes tops, and after the first time you do it, you simply copy the xml from project to project if you want to use the same settings.
This should be as simple as "import the project". Done. There should not be any extra steps. No settings fiddling. No magic files to copy. No plugin installations to intellij. Etc.
Sometimes, the tooling is even more important than language features IMO. It makes developers move quicker, find bugs easier and so on. I like the idea of Kotlin, that it can compile to jvm bytecode, javascript and native.
So in essence, I can use the same language everywhere in the true meaning of the word.
But I think the main strength of Kotlin is it being so damn developer-friendly compared to Java and Scala.
KTor [0] is the 'native' web framework for Kotlin, and there's also a full-stack framework built around it that just hit version 1.0, KWeb [1].
[0] https://ktor.io [1] https://kweb.io
Of course, I have checked Ktor before, but I don't know if I particularly fancy the way they are structuring stuff.
My main issue with the java ecosystem is that it's way to enterprisy for my taste. Everything is so unnecessary complex and hard to reason about, but I will take a good hard look at Kotlin before making my decision. The thing is that I am really interested in the native compilation that Kotlin offers that would be very beneficial for me and the only reason behind picking Python otherwise would be that it is a nice language and already installed on the linux environments it will run in.
Basically I will have a big api and drop (preferrably) a single binary to some machines that will talk to the api. These machines will generally be outside of my control. I have looked a bit on Rust but it seems a bit too low level and hard to work with. I have looked at Deno because it can compile down to a binary but using javascript for this project seems like a bad choice. So I chose Python at first because it seemed easy to get going with, had great tooling and is already installed in the environments I will be dropping a script in.
I prefer having one decent tool thats used across almost all projects like asp net for web
instead of 5 things with their own quirks and pros cons
Its annoying to have to relearn boring things on company change
Like orms, web frameworks, etc.
When you need custom solutions then you gotta put effort anyway
It has great Kotlin support, including Coroutines. I've introduced it into our team for a major rewrite and we are very happy about 18 months in.
The only thing I'd worry about is Java overtaking it.
Do you remember Coffeescript? There was a point in time where it had impressive adoption, and quite a bit of buzz. But then Javascript added features, and all of a sudden the tooling burden associated with Coffeescript just didn't make sense any more.
With Oracle's 6 month release schedule, that's a distinct possibility. Especially since Java can no longer rest on its laurels as a language (if Oracle doesn't want it to become Cobol 2).
ECMA 6 copied all the good parts out of both CoffeeScript and jQuery, effectively obsoleting both. Modern JavaScript is almost unrecognisably different from what it used to be, and that's a very good thing.
Ultimately, improvements to JS came along and took the wind out of CoffeeScript's sails. Then TypeScript came along and killed whatever interest remained.
And no, they serve totally different purposes. Coffeescript tried to make JS syntax and variable scoping behavior non-shit, while Typescript adds static typing while changing very little about the core language syntax.
* Android is a massive platform, and Android is basically stuck on JDK8 (or JDK11 with desugaring, wooo).
* iOS compatibility-ish. KMM is not ideal and mostly generates ObjC compatible objects (so generics are mostly screwed for Swift and writing iOS code is not ideal, but even being able to define common and enforced contracts is cool. iOS benefiting from projects like SQLDelight is super cool)
* Jetbrains hedged their bets, and Kotlin/Multiplatform is a solid option. Coupled with Jetbrains Compose for UIs as well as having the entire Java ecosystem available is very solid. Compilation for so many platforms, directly to native executables is cool.
* Java is still very slow to evolve. Kotlin has had data classes for a long time, Java recently added records. Inline classes offer some nice type safety at basically zero costs. Coroutines and structured concurrency are a beautiful way to work, and Project Loom would only build the foundations of that. Context extensions (while terrifying in the potential for spaghetti code they offer) are a useful feature, reified generics have some great potential.
Now, Kotlin has its disadvantages too. Compilation times are not that great (hopefully K2 fixes this partly), but it's relatively safe and is kind of more than just a JVM language by now.)
New Java versions are cut every six months since 2017 and since then has added a significant amount of new features, syntactical sugar, APIs, et cetera. Not only that, but they've also have been pretty aggressive IMO in deprecating and removing legacy. Very very different than the Java I remember working in years ago.
Kotlin is truly a pleasant language, both when you don't know it (although it can look a bit symbol soup-y at times for juniors), and when you fully know it.
And yet, the overwhelming majority of the Java community are still using JRE8 with none of these new features.
There are some that love this new pace of language development, but very few are actually using new features in production code.
We are migrating Java 8 -> 17 now and it’s been a right royal pain. I’m glad we didn’t jump to 11 LTS, and instead make one big leap. It might be a very long time before we do another LTS upgrade if they keep making poor migration choices.
Which likely means touching a pretty significant portion of your codebase for the upgrade, and then who knows what 3rd party libraries you depend on are expecting...
So it may be as simple as Find/Replace for some folks, for others, it might be a deep dark rabbit hole.
Not breaking things used to be Java's MO. Yes, that means a ton of legacy cruft built up over the years... but we used to be able to depend on Java to "Just Work".
Perhaps some of this is necessary. After all, the C# folks seem to have no problem breaking everything to add new features... but I'd assert the Java community as a whole is much less tolerant of breaking changes and rapid deployment of features.
There’s plenty of dependencies that provide javax implementations as well as jakarta.
The real difficulty for us was upgrading the Play framework since the Scala community likes to make breaking changes with every release.
I don’t know how well it works, but we have seen these kinds of projects, and they seldom work as is. Not sure how well Scala Native works, even though it predates Kotlin’s try. (Though scala.js is surprisingly good!)
But do you really think that a relatively young language like Kotlin with minuscule adoption (compared to java) will be better at this game then Java?
Java has a very good compiler to Js maintained and used heavily by Google (j2cl, part of their closure compiler toolkit), which can also output obj-c code. These are/were used heavily for porting their shared libs between basically all platforms.
For native, Graal is a very cool way forward benefiting all JVM languages (as well as scripting languages, its polyglot features are insane).
> Java is still very slow to evolve
Is it a problem? The majority of developers don’t like running after the language, even though it may seem so based on online hype circles.
Well enough, provided you stay within the known bounds. Hell, some people at Touchlab even went as far as porting Jetpack Compose to work on iOS (which, uh, I would not recommend in its current state), and it technically works. I probably wouldn't recommend writing all of your app logic in Kotlin, but being able to share the data layer is amazing.
>But do you really think that a relatively young language like Kotlin with minuscule adoption (compared to java) will be better at this game then Java?
It depends on where you're looking. Most modern android development will most likely be done in Kotlin. Backend work, for things started relatively recently, Kotlin is not surprising. But mostly, Java does not make iOS compatibility a goal. Java tell you "get a JVM and run our shit" (or get GraalVM and have basically an embedded JVM). Kotlin has two sides, and Kotlin/Native does not depend on the JVM.
>Java has a very good compiler to Js maintained and used heavily by Google (j2cl, part of their closure compiler toolkit), which can also output obj-c code. These are/were used heavily for porting their shared libs between basically all platforms.
j2cl still brings in a light JVM to the Web. Kotlin does not. It provides interop to the JS APIs as well as its own tech, but it's not meant to take your Kotlin code and immediately run it on the web (which is an awful, awful idea). You're still meant to write your browser specific code, your android specific code, your x86 specific code, etc. In any language you want, even! Write it in Kotlin/JS, or let your typescript consume the Kotlin/JS bindings. However, you can have a common base that'll work everywhere.
> For native, Graal is a very cool way forward benefiting all JVM languages (as well as scripting languages, its polyglot features are insane).
Graal is an extremely cool project, but with different goals. Write once, run everywhere is a lofty goal, but it only works on very similar platforms (Windows/Linux/OSX). Kotlin has taken a Write once, specialize what is needed approach.
Well, Gluon promises the same for JavaFX apps and there is a sample app actually downloadable from the AppStore.
> j2cl still brings in a light JVM to the Web
Not at all, it compiles to very optimized Javascript. Oh, and I forgot to mention that there is also teavm, which is not a VM contrary to its name -- this latter transforms java byte code so it works with guest languages as well.
> but being able to share the data layer is amazing.
why not just use swagger/openapi + auto generated models?adding kotlin native adds huge depenencies relative to the tiny benefit of a datamodel that can be auto generated to native code imo
Which dependencies ? If you already have Android devs, on the iOS side it adds a single swift package, and similarly so on Android.
> It does not handle repositories, data fetching, caching and combining, it does not handle a SQLite database.
right, but you can use that to autogenerate all those things (have done it before) with a few additional tools (code templates) and have it all (repositories, fetching, caching etc) automated (there is a limit of course, combining might be a bit hard in that case)this is really useful though because now you can deploy to more platforms in their native languages using idiomatic code, and switching to newer techniques, e.g async/await can be done instantly just by updating the source template which any team can download and compile themselves
> Which dependencies ? If you already have Android devs, on the iOS side it adds a single swift package, and similarly so on Android.
for ios, its including basically the whole kotlin runtime and additional support (kotlinx-datetime, etc) in that single package (xcframework), then add a lot of data classes and things start to add up... for us its one of the biggest single dependencies on our ios appI think a fixed N month release schedule is much healthier than what Java did before, which was wait until everyone was on the bus in order to ship, which resulted in multi-year delays.
The thing is, they're all optional. You're never going to use any of these features if you don't need them. Your code can stay as a nested list of calls. But put them all together, and you can have code that is super explicit about what it needs to do, without having to re-pass things that are already there.
Even if it makes things better only for, say, 10% of users, but doesn't make it worse for the other 90%... the users will take that every N months for as long as the language authors can do it.
> having the entire Java ecosystem available
and, in the same point:
> Compilation for so many platforms, directly to native executables
Does this mean you can compile pretty much any Java libraries to non-JVM executables?
If your target is JavaScript, you can use JavaScript dependencies (sort of: they won't have any type bindings so you will need to code then yourself) but you can't use any JVM libraries on it.
So yeah, I can see Java potentially overtaking Kotlin.
Scala had a massive much more prominent project to rely on - Spark.
Kotlin is getting traction because of Android.
Kotlin is seeing major adoption on the server.
So in a sense, from a tooling and compiler perspective, they're orthogonal products, similar (I guess) to C# and F# on the .NET CLR VM.
I think Oracle is catching up slower than Kotlin has been evolving in terms of new language features. Most of the stuff they add to Java (including most of the stuff they are talking about adding), Kotlin has been doing for quite some time. Kotlin is pushing out minor releases about every 3-4 months and major ones pretty much every year. Basically, they do 2-3 minor versions in between major ones. The pace is relentless.
If anything, Kotlin could use an LTS release because it's actually getting hard to keep up with the ecosystem.
A slower pace might be helpful with that. The main issue with frequent releases is that many libraries take weeks or even months to update and don't necessarily work well (or at all) with newer Kotlin versions. I've had repeated issues with e.g. code generation plugins requiring specific version of Kotlin breaking because some other library suddenly requiring something newer. So that then starts blocking a lot of library updates. We've actually put some effort in unblocking some of this for some of the dependencies we have by creating pull requests.
But I'm excited about the next 1-2 years. I expect Kotlin native to stabilize and grow beyond just being a thing for IOS. I also expect wasm support will become usable in that time frame. I hope to be able to use it with WASI for edge networking or serverless stuff. And maybe even some command line tools. IMHO Kotlin has potential for data engineering as well. There is actually jupyter support for Kotlin. And some machine learning frameworks. It's becoming a proper full stack language.
Both Scala and Kotlin can reuse Java tools, but with Scala it's awkward (and some tricky generics simply don't compile with Scala).
E.g. if I need to parse something, I search for "how to parse that in Java", not "... in Kotlin".
What? Do you have any example that wasn't fixed years ago?
Any non-trivial Scala app will consume dozens if not hundreds of Java libraries, without any issue.
I cannot find the issue now, but I remember I was trying to use gae-mapreduce-java library (built on top of Hadoop interfaces). It couldn't compile with Scala.
I would assume such a bug was fixed in Scala 2.x given the prevalence of the language in the hadoop ecosystem and big data frameworks.
Moreover Scala 3, was the opportunity to fix many previously-unfixable corner cases.
That would be always. Tooling makes or breaks pretty much every language. Language itself is just a syntax that you write in.
With streams and the var keyword, Java is also not that much more verbose anymore than Kotlin and the code completion, error detection and refactoring capabilities are much better in Java. In Java I can also be sure that if I directly access a property on a class, there will be no side effects or expensive operation while if I use a getter, there might (and I dont have to write them either due to code generation or Lombok). So the parts where Java is still more verbose actually add clarity (and again, due to the IDE, you rarely have to type any of it): I dont really get why I would leave out the explicit types in the source code, only to be added again as non-editable, weirdly positioned text by the IDE (which is not there if just wanna have a quick look at the code on GitHub).
Records ergonomics is terrible if you want to replace data classes with records, even if immutable is OK. You need to write builders. You can't really write code like
new Person(id, null, null, null, lastName, firstName, null, null, null, null, null, null, null, age);
Withers can help with that, but they're not even in any kind of experimental shape right now, so we have many years to wait for it.Right now records are only fine for very simple use-cases like Point(x, y). Or if you want to write or generate builders. But at this time you don't really save anything, if you can generate a builder, you can generate a class as well.
If you have data class with more than 5 fields then you have it wrong. Builders are like lombok and like field injection: hide poor class design.
If using a class hurts, it is badly designed and should hurt until it is refactored/split up into consumable parts.
I use records extensively, but those are in most cases converted classes which were small and immutable. JPA being one of the exceptions (a stuck in the past spec)
There are very few cases where you need to work simultaneously on that many columns.
One my pet peeves, shit like this sucks :
var f = MyStupidMethod();
this line has zero readability
you're not writing code for compilers, you're writing code for people
Fundamentally, this is a story of technology getting better (type inference in compilers) so humans can do less work. We're not pushing the work to some other human place. We're not losing anything in safety, and we're gaining in readability, boilerplate and expressiveness. Its progress!
i agree, thats why i don't want to clutter my code with annotations designed for the compiler, not humans. Type inference is a _good_ thing for readability.
Since the actual size of the code base isn't an issue in the modern world, I personally like that the Kotlin ecosystem discourages the use of esoteric names and non obvious abbreviations. Once you write and read enough of it you stop making poor choices.
> var conn = openDatabaseConnection();
Are you actually confused about what conn represents? Does it matter what the exact type is, if you already know what it is and what you can do with it?
val foo = new MyReallyLongUglyFactoryClass()
instead of: MyReallyLongUglyFactoryClass foo = new MyReallyLongUglyFactoryClass()
And you never have to use type inference. If you have a method where the return type isn't as obvious, you can always annotate it explicitly: val foo: Int = doSomeThing()
Do people exercise judgement about when to make the type explicit? Eh, depends on the team. But if you combine type inference with established conventions in a particular codebase (e.g. I/O calls always return IO[Foo]), you will rarely be left wondering what the type of something is.> var f = MyStupidMethod();
Yeh, but so does:
List<MyWeirdoFooClass> myWeirdoFoos = new ArrayList<MyWeirdoFooClass>();
Sometimes it's clear what it is. Sometimes you don't really care. Sometimes you do. You can still make it explicit when you need it to be explicit.I have no experience with Kotlin yet, but I recently did some Java after years of js/ts, and was nearly driven mad by constantly having to convert different kinds of collection. String[], Array, List, Iterable, Stream... and every library expects or returns something else, so you're constantly converting these things.
Any of them would have been perfect if only it had been supported by everything, but because of Java's long history of constantly inventing newer and better ways of doing things, libraries written at different times support different kinds of collections and you constantly have to convert them.
In the different collections, you see a history of 20 years of developer experience improvements coupled with an almost religious emphasis on backwards compatibility in the language and standard library.
In every (long term) JS/TS project I worked on, about 20% - 50% of the development time went into upgrading libraries and keeping everything running on the current node version. It was maddening. Stuff like the different types of collections are a very small price to pay, IMHO, if you can be sure that your current Java project will with very high probability also run on a current JVM 10 years from now.
That's actually why I prefer List, because a List is an interface and can be an ArrayList or another type of List and I don't care about the implementation, as long as it's a List. But Array is an array that's not a List, and Iterable is also an interface, and I think a List is also an Iterable, but I'm not sure, and I suspect not every Iterable is a List. And then there's Stream which should have been a set of convenient functions that are part of List or Iterable right from the start, but they're not. And what the hell is a Spliterator and why do I need one to turn an Iterator into a Stream?
I understand that there are reasons for this, and I totally agree that the js/ts dependency situation is far from ideal, but it's still maddening to have so many different collection types, when js/ts just has an array that's not even an actual array, but it still works fine, and all the new stuff just gets added on top of it.
One of these days someone should invent a new programming language that finally gets all of this right once and for all, but I bet people will find ways to improve it again.
It "works fine" for basic cases but it doesn't give you anything like the same capabilities. JS/TS have no easy equivalent of array or stream, anything you could write in js/ts you could write in Java by only using ArrayList. Java's collections library is big and complex but almost everything that's in there is there because it has a use case, and while it's showing its age now it was well designed for its time.
> One of these days someone should invent a new programming language that finally gets all of this right once and for all, but I bet people will find ways to improve it again.
Your best hope is a language with Haskell-style typeclasses where you can not only add new implementations of existing interfaces, but also retrofit new interfaces onto existing implementations. No library is going to be perfect from day 1, and a big part of the reason why Java Stream is awkward is that it had to be retrofitted into the existing collection library.
Stream usually is not used other than for call chains. Like list.stream().map(...).toList();
I write Java for many years and I can't really say that this was source of pain for me. I agree that streams could be implemented with better ergonomics, but Java designers wanted to provide us means to use parallel computations with one extra word, so here we are. I've yet to find a place where I'd want to use that.
I used collection classes in Java 1.4 and I'm using them in Java 19 and I don't think that anything changed in that regard. Collections are the same.
Yes, but every library I use requires or returns a different one.
> list.stream().map(...).toList()
But why not list.map(...)?
I have had some internal teams try to use Iterable or arrays, thinking they needed to be optimized. Almost without fail, those are from teams that stall out and never actually finished what they were doing.
I have seen case studies of giants that actually needed to optimize down to arrays or buffers, but this is often not the case. I'd imagine unless you are trading or gaming, you can get more than enough mileage out of List. (Specifically ArrayList. LinkedList is usually a sign of premature optimization without having actually benchmarked what was running.)
In theory this is possible to approach with annotations but I found it hard to keep up in a large codebase.
I think the point is to be calling them asynchronously. Unfortunately async programming is kind of contagious and difficult to shoehorn into sync stuff but if it’s written asynchronously there’s usually a reason.
The extra sugar on top is super nice too - listOf, mapOf, x to y, apply… I think it just makes code that much cleaner. None of these are make or breaks but together they work amazing. A lot of what I work on professionally is still on Java 8 or 11 so I don’t get any of the cool features like record types.
Most of the OSS I do is Kotlin bedsheets I just enjoy writing Kotlin. I don’t feel that way with the older versions of Java (I’m starting to with the newer ones).
Java spring does the same thing: if you use an unboxed type (int instead of Integer) and the client passes in null, the handler will never be invoked.
I've found myself with this a lot on "progress" data classes where I will be building up a set of data over several expensive calls. I /could/ make a new type per current state of that, such that I never have nulls. I /could/ make it so that every field is Optional. Or, I could work on a convention that fields are filled out in order, and after the first null, nothing is available. In many years, this has not been where null pointers hit me.
Theoretically everything worked. However, the end result is (1) the customer didn't get their renewal notice and (2) we got a validation error instead of (maybe) a NPE somewhere. So what exactly are we accomplishing here is my broader point.
In a case where the client expects an immediate response (http GET) getting “400 validation error: foo is not allowed to be null” is a lot more meaningful than “500 Null pointer”.
In general I try not to model invalid states - less mental overhead (no one has to tell you xyz can’t be null it just cannot be null).
Ofc this is a matter of opinion and the lines get blurred as soon ad you move stuff to runtime.
While Javas checked exceptions had a lot of problems, at least there was a way to declare the output (errors are ouput) of a function on a type system level.
With Kotlin, that's just gone. You can fiddle with Result type (or make your own), but you lose a lot of the ease of "bubbling up" errors.
How do you handle errors? (I wished Kotlin had error handling like Rust, which is the best that exists, imho)
I've only used Kotlin on Android, but from what I remember co-routines are not necessary to use. If you don't like them, then don't use them.
Kotlin has a number of advantages over Java. The biggest of which is built-in optional typing. It's also really nice that everything is expression instead of a statement. Library functions like `let` also make code a little nicer to write. And stuff like data classes and better property initialization are icing on the cake. In my opinion, you can write Kotlin exactly as you would Java, but the development experience is much more polished.
Typing is not optional, everything in Kotlin is typed. What the language has is type inference. The following lines have the exactly same types:
val teens = people.filter { it.age < 20 }.map { it.name }
val teens: List<String> = people.filter { p: Person -> p.age < 20 }.map { p: Person -> p.name } final var teens = people.stream().filter(p -> p.age < 20).map(People::name).toList()However as any async/await implementation it will obviously still have challenges. The "colored" world of functions means users need to know the differences between suspend functions and other functions, and using something wrong can lead to performance issues.
One thing to note is that with the upcoming of virtual threads (Project Loom) in Java itself, the need for using coroutines might become much smaller. You might just end up using them for UI work, and everything else could use virtual threads. That could simplify a lot of things.
And since it is API compatible with things that are already supported by co-routines, pretty much all you need to do is change your custom co-routine contexts to be backed by virtual threads rather than actual threads. This should just work even without explicit support for this in the co-routines library (it's just another thread pool). But they obviously will add explicit Loom support as well as it makes sense to make co-routines be virtual threads pretty much always.
When that ships, all current kotlin code that uses co-routines will be using virtual thread on a Loom capable jvm. It already has structured concurrency and all the rest so all of that will continue to work. And some of the blocking Java stuff that you currently need thread backed co-routine contexts for, will stop being blocking so you can stop doing that. But it will still work of course.
If you already use Kotlin coroutines - fine to continue to use them. But I imagine for a new project you might want to opt to build on virtual threads instead of coroutines.
Use a proper asynchronous framework and then the only thing you need to worry about is avoiding calling synchronous code on your main thread (i.e. dispatch it to some thread pool backed co-routine context). Not that hard. And you can get rid of a lot of that stuff by gradually switching to non blocking versions of whatever you are using.
If you are exposing class properties without accessors in Java, that's not necessarily a great thing. Especially if your state is non final (i.e. mutable). In Kotlin, you'd use vals (or vars if you really have to) and by default they just behave like they have getters/setters. But you don't have to spell it out. And you can override the getters and setters. Just like you would in Java. It's just the distinction is not there. It's basically a less hacky Lombok.
The weirdly positioned text that your IDE adds are called hints and you can turn them on or off as you please. They are there to help you. Type inference is a nice thing: it means you can read the hints without having to spell out what is what. Java has type inference too but it's a bit more limited and more verbose. There is no uncertainty about what the type of anything is in either language: exactly what you specified (but just once in Kotlin's case).
In general Kotlin is more concise, checkout these comparative examples:
https://stackoverflow.com/questions/34642254/what-java-8-str...
This probably sounds a bit weird, and it surprised me at first.
Unfortunately measuring build times is extremely noisy: different people build different things. They do so at different times. On top of it we have incremental builds (so only some part, depending on the modularity and some external and in-house optimizations, gets rebuilt). The incremental builds depend on cached artifacts that need to be downloaded, and many more complications.
The result is that if you look at the graphs the values jump up and down based on time, days, moods, network, and many more causes, plenty of which we have no idea what they are.
I have not yet seen a successful attempt at cleaning this data up so that some number would be worth publishing. We could try building some toy example that we separated. But that won't be useful to guide our work on build times, nor do I think that it will be valuable to share with the community.
1. Then how do you know builds are slower?
2. How will you know that the things you are doing to improve build times are working?
> jump up and down based on time, days, moods, network, and many more causes, plenty of which we have no idea what they are.
3. Performance being highly inconsistent is actually a (significant) performance problem itself and worthy of reporting, analysing and quantifying. (ranges, averages etc.)
> not yet seen a successful attempt at cleaning this data up so that some number would be worth publishing.
4. Not sure why you think they need to be "cleaned". Again, numbers being all over the place is likely an important data point in itself, an even if it isn't the raw data would be valuable.
> We could try building some toy example that we separated.
Why? Just do a clean build on a local machine. Report real and user+system time. (man time). Repeat.
2. We have more specific metrics for parts of the build that we know would improve. For example, migrating annotation processors to use KSP instead of KAPT makes modules build faster. (This doesn't mean we can easily expect a different trend line in the top level graphs since in the meantime the amount of Kotlin code is changing.)
3. I'm not sure what your experience is, but according to mine it will be a "nice to have" but un attainable or at a cost of real user improvement. For example, if a lot of people build at once network caches might be slower. How does one remove that noise? Up the caches - that's more money and other updating issues; try to take it into account in data? maybe, but that's one of many issues (going back to the next point)
4. The commenter asked for numbers on builds. I don't think a graph that's jumping up and down due to the complexities and optimizations of Meta' build architecture is useful to anyone outside Meta. I think deducing results about Kotlin build times from it would be deceiving.
> Why? Just do a clean build on a local machine. Report real and user+system time. (man time). Repeat.
This number is not stable. Even if you shut off network caches and other similar things. But more importantly - this number is not useful. Our users rarely have that experience. One of the common sub goals we have when dealing with build times is to minimize the times someone will have to do a clean build.
[1] https://engineering.fb.com/2017/11/09/android/rethinking-and...
How much shorter? How large would the code be in pure Java?
> On average, we’ve seen a reduction of 11 percent in the number of lines of code from this migration.
So that’s an extra million lines of code that didn’t need to be debugged.
Hardly any code that would actually have bugs.
A lot of the saves come from `?.` replacing null check chains. Many more from the shorter constructor and field initialization syntax (you can get a bunch of things in one short statement). A bunch more from usage of standard library lambda taking functions (`first`, `single`, etc.). Some more just comes from shorter lines due to less explicit types. These are all more some guesses due to conversions I've seen, and not the result of accurate analysis.
We also use very little Java streams. A combination of Java 8 arriving late to Android code, and the preference to avoid extra allocations and inefficiencies streams can cause for shorter lists.
I've seen the same on my own converted Java code. Less lines of code. But you also get lots of a really short lines. And some formatting actually spreads things out over multiple lines for readability. E.g. I use intellij's build in "put arguments on separate lines" a lot. Especially with named arguments, this is just nicer to look at. It's the same or less code but more lines. But less dense code.
A more interesting metric might be simply the byte size of the source files. I would expect that to be slightly better than 11percent. Maybe closer to 20-25%.
Kotlin might seem a little weak to someone returning from a scala deep dive, but where it really shines is in creating a large overlay between the convenient path and the pedantic path. It removes many "in the small" tradeoffs that you just take as a given in java. It's the "better java" that groovy was not.
I dismissed Kotlin for this very reason years ago. But after seeing bad Scala codebases, Kotlin feels like a breath of fresh air.
That language, just like with Swift and Haxe, they remind me of the good old Flash/ActionScript 3, i loved that language so much
It's imo the best form of a programming language one can come up with, very easy to learn, read and write
I wish kotlin-native story was better, it's a shit show, super slow to compile, and they decided to go with Gradle... it is on the same level of confusion and bloat as CMake, a pure mistake
I was holding off on KMM before, but I've started looking lately and its extremely promising. There is still a lot of ecosystem that needs to be built out (e.g. you need to find cross platform / pure kotlin implementations of URI or Color), but it is shaping up to be the leader in the space IMO.
There are actually 3 layers in play for each platform: The pure kotlin shared layer, the "native" kotlin shared layer, and the native-native layer. That second layer (the native kotlin shared layer) is platform-specific kotlin. So on JVM platforms thats the normal Kotlin you are used to, and as such it has access to Java. On iOS that second layer is Kotlin/Native, and you actually have access to native _bindings_ to the native layer (i.e. I can call into UIKit, but in Kotlin code). If you put your "native" code in the second layer instead of the native-native layer (which on iOS is Swift/objective-c), then you can move that into the innermost layer if/when it makes sense over time, and the frontend is none the wiser.
Here is a good visual and explanation (the rest of the presentation is a good overview as well, but I think this slide is the best part of the video): https://youtu.be/2yd6rVJdICU?t=1749
EDIT: I should also note that nothing prevents you from creating "glue" bindings in the pure kotlin layer using either expect/actual or an interface, and then reifying that using some JVM thing in the second layer, and writing the other platforms more from scratch to fulfill the contract.
> I can call into UIKit, but in Kotlin code
just a note for those who've never used kotlin native: its generating obj-c code when compiling for ios, so you cant use any swift-based apis that arent exposed to objc from appleuikit isnt going anywhere for a long time probably, but its an important caveat
EDIT: and a nitpick. Its not generating any objc code. Its generating a binary which is exposed to swift using the same mechanisms that objc libraries use. I.e. it compiles to a native binary (an xcframework or framework, I forget which). It doesnt transpile.
> Its generating a binary which is exposed to swift using the same mechanisms that objc libraries use. I.e. it compiles to a native binary (an xcframework or framework, I forget which). It doesnt transpile.
right, though the native library (xcframework) exposes its interface as objc objects (.h header files), since swift only knows how to read c and objective-c modules afaik...Java doesn't have that and it's kind of an increasingly dead language for mobile development. Some older android projects still use it. But Meta is late to the game of migrating away from it. Many companies did that years ago and the vast majority of new Android applications is using either Kotlin or Flutter/Dart.
I have good hopes for Jetbrains extending the jetpack compose/compose desktop/compose web ecosystem to IOS at some point as well. That would create some interesting possibilities in terms of targeting just about any platform with kotlin from a single code base. It's a logical next move for them and they've been laying the groundwork with their multi platform compiler strategy for a while now.
the tradeoff for high level languages is supposed to be that you gain a lot of expressivity (requiring fewer lines for a developer to express their intent) and you lose performance because of the multiple layers of abstraction required.
10 million lines of code for an Android app, or family of apps, feels like the tradeoff has definitely not born fruit.
operating systems written in Assembly have far fewer lines of code than this.
You have to factor in Instagram and WhatsApp, but still...that's a lot of code.
Windows XP is smaller than the Facebook app for Android...
You're off by an order of magnitude. Hundreds of developers? Sure. But Uber has thousands of developers.
If it still doesn't seem worthy of 'thousands of engs', imagine breaking down every point in the list to the level of detail the author broke down payments. And then apply the logic in the top comment mentioning backend systems + teams to support other processes (and internal tools needed).
Edit: sorry too much caffeine got me edgy.
UI pages must be immense in terms of lines of code.
(though hard to generalize to other parts of the app, bc payments is inherently glue-y)
constantly wonder what kind of support layer would make UX integration user-friendly, but also developer-friendly
The notion of Kotlin being a mobile only thing emerged out of the happy accident that the need to get rid of Java was simply felt so deeply in Android that developers were all over Kotlin even before it was released properly. Backend developers waited a bit longer.
As a drop in replacement for Java it made a lot of sense there. Google at the time had the whole platform stuck on Java 1.6 while they were engaging with Oracle in the courts. So a lot of the nice stuff in 1.7 an d 1.8 was not usable and forget about all the stuff that was added to Java and still is being added. Most of that you could get with Kotlin right then and there. So, people jumped on it.
For the same reason, people have been using Kotlin with Spring since about the same time. Google and Spring made it official around the same time as well with Google outright labeling it as the preferred language for Android (while not cutting off Java) and Spring just adding an enormous amount of Kotlin specific features and extensions starting with Spring 5 and Spring Boot 2. At this point both have documentation for both Java and Kotlin. There are really no downsides to using Kotlin on the server. You can do everything you could do with Java and you gain access to a lot of easier to use stuff than the Java equivalent.
I've been using it since before Spring supported any of Kotlin. Worked fine then and it only got better since. The language alone is worth switching and once Spring started actively supporting it, it only got better. I write asynchronous co-routine code with Spring by default. Mostly it just looks exactly like synchronous code. The only way you can tell is that my controller functions are suspend functions. Spring takes care of the rest. That makes everything easier: more readable logic, error handling, etc.
I like Kotlin, but this was pretty funny to me: I imagined walking into a meeting and trying to tell colleagues we needed to move to X because it was popular, then yeet out the ole “efficient”, mic drop & moonwalk out
I find this similar to how rust is being added to the Linux kernel.
I had a few encounters with facebook's opensource code (mostly around react-native) and it is generally subpar in my opinion.
I'm interested in how they avoid "Roman empire syndrome", as in owning more software than they can maintain while actively expanding it.
How do you implement these features for more than a billion active users in less than a million lines of code? Even 100 million sounds like a low estimate.
Why does the Linux kernel have more than 30 million lines of code?
You could ask if these features make sense. But in the end it is their business decision to make.
translations (left to right text and layout: https://sy-sy.facebook.com/), different laws depending on jurisdiction; for example privacy/moderation/takedowns, taxes/payment/invoices if you are selling anything, accessibility
If you ever start a company and you have the long-term goal to offer your service in multiple countries I would advise you to launch in at least 2 countries, preferably supporting different languages.
iOS 310.6 MB
Android 367.7 MB
They might've started with PHP, but even that stopped being a thing once they switched to Hack, their home-grown language, ~10 years ago.
[0]: https://engineering.fb.com/2021/04/29/developer-tools/rust/
> For business logic and relatively stateless applications, the Hack ecosystem has the highest level of automation and support at Meta and is the recommended language.
On a related note, their Rust usage has grown significantly:
> For performance-sensitive back-end services, we encourage C++ and Rust. Rust is a new addition to this list. There’s a rapidly increasing Rust footprint in our products and services, and we’re committing to Rust long-term and welcome early adopters. For CLI tools, we recommend Rust. This is a new recommendation for this year.
https://engineering.fb.com/2022/07/27/developer-tools/progra...
Jetbrains' C#