story
Also, java is solid as a language. Sure, it is not the most modern one, but it is reasonably productive, has great tooling, is very performant and perhaps most importantly, it is observable in a very fine way.
I agree that these things are just complex. But, what's interesting to me is that I fully agree with your second sentence and I see it as an indictment against the Java ecosystem around these operations. The common Java frameworks, IMO, serve to make the trivial stuff even more trivial, but then make the complex stuff even more complex. It's exactly the opposite of what I want.
Let's look at JacksonXML for serialization.
Here we have a framework that uses runtime reflection to more-or-less guess how to (de)serialize an object. Figuring it out at runtime is a tough engineering choice already, because it pretty much immediately means that you're going to have to figure out a caching system for what types you've already analyzed, so that performance isn't terrible. And we all know how hard cache is.
But, on top of that, Java uses type-erased generics, so you can't actually reliable use runtime reflection to figure it out! But the compiler certainly won't tell you it's a problem, because Jackson will try to (de)serialize anything you throw at it. You don't even need any annotations for most stuff. It "just works" (TM)... until it doesn't.
So, if you use generics or inheritance or any non-trivial mapping, you have to write a custom serializer. Okay, that's no big deal.
But then you realize that JacksonXML will IGNORE time zone information on an incoming serialized date field that is encoded as an ISO8601 string and just use the current JVM system time zone. Because why the fuck not, I guess?
So, in other words, Jackson makes already-trivial things a little less verbose, it makes non-trivial things a pain, and it even makes some things that should be trivial into a pain.
I can play the same game with JPA/JDBC. In particular, it also does really stupid things with time zones and date-time types. It also can't really handle complex types for similar reasons to JacksonXML.
> For what it worth, java has a really high quality ecosystem for all these things - I would be really interested in what you think as an alternative.
My favorite languages to work with at the moment are Rust, Swift, and Kotlin. All three have way better serialization stories than Java. Rust has serde, which is like JacksonXML, except it's compile-time and your types have to implement a Serialize "trait" (interface). I truly think I'm being honest when I say that I've NEVER had a runtime serialization (type) error in my Rust projects that have used Serde. If it compiles, it works. The same is almost true of Swift and Kotlin (with kotlinx.serialization), except I do think I've encountered some runtime issues in both of those (IIRC, there was a surprising limitation with Swift Dictionary keys needing to be Strings or something). Kotlin's approach is my least favorite of the three.
When it comes to ORM/SQL stuff, I've been using a query builder in Rust that's a delight to use. Basically, when you execute a query, you use the type system to indicate the type you expect the returned rows to be. As long as the type implements a `FromRow` trait, it will "just work" or return an error value or crash (you can choose to call a "safe", error-returning, function or a "crashy" version of the function that assumes success and crashes otherwise). Rust has ad-hoc tuple types, and the library implements its `FromRow` trait for all standard types as well as all tuples up to 13 or so elements, so often you can just write something like `let (id, name) = query<(u32, String)>.execute();` and it will just work. If the `id` column is `null`, then the call will FAIL instead of doing something insane like just making up data (like JDBC returning `0` instead of `null` for nullable int columns).
The Rust query library I'm using is the perfect example for the point you made earlier about making trivial things trivial. This library does require a little bit of boilerplate to implement `FromRow` for your custom object types. It's not really worse than JPA for the simple cases, but it's WAY ahead when it comes to dealing with the non-trivial cases.
> Also, java is solid as a language. Sure, it is not the most modern one, but it is reasonably productive, has great tooling, is very performant and perhaps most importantly, it is observable in a very fine way.
Credit where it's due: Java does have phenomenal tooling and it's very fast (except when you use the frameworks that we're discussing...). I think I'd lump in the observability with the phenomenal tooling.
But, no, I wouldn't call it a solid language. It's far too primitive and bug-prone for writing robust applications. The null issue doesn't really need to explained, the ease with which we can leak resources from Closeable things, the awkwardness of the type system (e.g., being unable to implement an interface for types you don't own, being extremely tedious to define "newtypes" like a `NonEmptyString`, etc), the incompatible-yet-ubiquitous use of runtime reflection and type-erased generics, etc.
I'm sure you're a Java expert, but I'd wonder how many years you think it took you to get to the point where you feel like you aren't bitten by all of the things I've described in this comment. If the answer is more than 1, then I'll go ahead and assert that Java is not a good programming tool for the domain in which you work. I've been working with JVM languages for about 5 years now, and I'll say that I'm now familiar enough to avoid these traps most of the time, but holy shit- it should not have taken nearly that long.
Regarding Jackson and JPA the only thing I can tell about these is that their age shows, and they come from a domain and age where the (in my opinion, bad) POJO and Java Beans conventions originate. So I fully agree that things could be much better, and hopefully there will come a renaissance replacing these tools with modern java equivalents, that don’t rely on runtime magic as much, and will use the modern datetime APIs by default, etc. Serialization is especially in need of a huge revamp, hopefully records will make it much better.
Regarding ORMs, have you by chance tried JOOQ? You may prefer it over JPA.
Also, just a small note on Rust - I find it to be an excellent language, but I really don’t think it fights in the same domain as Java. Systems programming is fundamentally different. So writing a huge business application in Rust (or in C++, equivalently) is a suicide mission in my opinion — initial write time may indeed be low for an experienced Rust dev, but with the often changing client requirements that mandate quick changes touching everything, the low level details that leak into the high level view of the app will slow one down (now you also have to change the memory model because this lifetime has to be extended, etc). But I only mention that as an explanation for why Rust is not a replacement for the huge, ever-living business app domain (at least for me)
So all in all, I think that Java fights a good fight, it remains religiously backwards compatible which is painful at times, but is perhaps the biggest value there is; but it is improving with a huge pace with records, sealed classes (giving us algebraic data types), upcoming `with`ers will provide a good syntactic sugar for “modifying” immutable objects, and full-blown pattern matching is coming built on top of these. But the real deal happens under the hood, Loom will make blocking codes magically non-blocking and Valhalla tries to heal the rift between primitives and objects and will provide excellent performance boost. So the part I actually like and defend about Java is this one, not the historic baggage it comes with. But I also work on CRUD apps, and that’s not an exciting domain no matter what.
I didn't pick up any high emotions, but I get it. For some reason, I get fiery about this stuff, too. I don't know if it's that I get equally worked up no matter what I'm arguing about, or if it's worse because I'm passionate about computers and programming.
> Regarding ORMs, have you by chance tried JOOQ? You may prefer it over JPA.
I haven't used it, but I've read their docs and API. It looks great, albeit very large. It also preserves some of the... conventions... from JDBC and JPA that I find egregious, like converting null to actual values when mapping query results (https://www.jooq.org/doc/3.15/manual/sql-execution/fetching/...). At this point I have to assume that Java devs actually prefer this behavior, but I think it's crazy- if I expected a non-null int and I read out a null, I want to crash- not pretend like I got a valid int...
> Regarding Jackson and JPA the only thing I can tell about these is that their age shows, and they come from a domain and age where the (in my opinion, bad) POJO and Java Beans conventions originate. So I fully agree that things could be much better, and hopefully there will come a renaissance replacing these tools with modern java equivalents, that don’t rely on runtime magic as much, and will use the modern datetime APIs by default, etc. Serialization is especially in need of a huge revamp, hopefully records will make it much better.
> Also, just a small note on Rust - I find it to be an excellent language, but I really don’t think it fights in the same domain as Java. Systems programming is fundamentally different. So writing a huge business application in Rust (or in C++, equivalently) is a suicide mission in my opinion — initial write time may indeed be low for an experienced Rust dev, but with the often changing client requirements that mandate quick changes touching everything, the low level details that leak into the high level view of the app will slow one down (now you also have to change the memory model because this lifetime has to be extended, etc). But I only mention that as an explanation for why Rust is not a replacement for the huge, ever-living business app domain (at least for me)
At the risk of coming off as a combative asshole, I'm going to pick on you for second because it's relevant to the part about Rust.
In your previous comment, you asserted that serialization and ORM/data-access is just as difficult in every other language as it is in Java. You also asserted that Java has a "really high quality ecosystem for all these things".
But, here you're acknowledging that serialization and data mapping are "showing their age", follow "bad" conventions, "could be much better", shouldn't rely on runtime magic, and are in need of a "renaissance".
Even though I wasn't advocating for Rust as a great fit for enterprise web apps, I will say this: I think you're rationalizing. You've already made up your mind that Java is good for application development and has a good ecosystem. But I think I can make a solid case that Java is a primitive, unexpressive, bug-prone, language with a large-but-mediocre ecosystem (where the most widely used parts need a "renaissance").
Even after I argued (convincingly, I assume, since you changed your expressed opinion about serialization and data-mapping in Java) that Rust is better at both serialization and data-mapping, you're asserting that Rust would be a suicide mission for a large scale business app. Well, it's apparently better at two of the most fundamental parts of any web app, so I think it's looking pretty good as far as suicide missions go.
Hell- Java doesn't even have single-thread concurrency!
Rust's type system allows us to express more elaborate abstractions with less code than Java (enums vs sealed classes, type classes vs adapters and decorators)
Rust makes concurrent code safe. No need to remember to use mutexes or to make inefficient copies/clones of "immutable" classes- if you write code that would cause a data race, it just won't compile. In Java, you'll just get bugs and corrupt data.
In Rust we'll never get NPEs.
So, I don't agree with your assessment at all. Writing a large enterprisey business app in Rust will likely run faster, have fewer bugs, use less memory, and even scale out better. If you're hosting your app on a cloud provider, it'll cost you less money to operate as well.
I think that you just want to believe that Rust would be worse than Java, and I think the cargo cult agrees with you. But, having done significant work in both languages, I think that's incorrect. My Rust code is generally an order of magnitude less maintenance than my Java and Kotlin code have been. Furthermore, it took me LESS time to become truly proficient at Rust than it has to become proficient (as in writing relatively few bugs the first time) in Java and Kotlin.
Some day, Valhalla will land and Java will get some things that other languages have had for a while. And eventually, Java may even become a solid language for its major use-case. But today is not that day. And today we have languages that are already better. Literally everything you listed (sum types, records, pattern matching, and non-blocking) already exists in Rust and Swift (and Scala, and Kotlin).
That's true but which is more flexible for the "ever changing living business app domain" the GP is alluding to? You seem to keep ignoring this part, flexibility matters. In rust is easy to code yourself into a corner and spent lots of time rewriting stuff over and over.