News for hackers. Stuff that matters.
Re:Rating 26 years of Java changes by dkarl on Saturday October 11, 2025 @04:12PM (#45552280)
Definitely underrates the impact of annotations. I'm personally not a fan of the way annotations are used to implicitly wire together applications, but I have to admit the impact. Maybe 5/10 is fair in light of the wide range of positive and extremely negative ways annotations can be used.

So many of these features were adopted after they were proven in other languages. You would expect that since Java took such a slow and conservative approach, it would end up with extremely polished and elegant designs, but things like streams ended up inferior to previous developments instead of being the culmination. Really disappointing. Java is now a Frankenstein's monster with exactly as much beauty and charm.

Re:Rating 26 years of Java changes by dhosek on Saturday October 11, 2025 @04:20PM (#45552343)
I used to joke that the direction spring was heading was that you’d have an application be a boilerplate main method with a dozen lines of annotations. Then I actually encountered this in the wild: we had an app that sent updates from db2 to rabbitmq, and the application literally was just configuration via annotations and no actual Java code other than the usual spring main method.
Re:Rating 26 years of Java changes by 9dev on Saturday October 11, 2025 @05:06PM (#45552711)
Is that strictly bad, though? Being able to run an enterprise service by setting configuration values declaratively, and get all the guarantees of a well-tested framework, seems like a pretty good thing.

Yes, it’s weird how that’s still Java, but using standard components and only using code as glue where it’s absolutely necessary seems very similar to other engineering disciplines to me.

Re:Rating 26 years of Java changes by SkiFire13 on Saturday October 11, 2025 @06:13PM (#45553113)
I think the general adversity against this specialized configurations is that they often tend to be fairly limited/rigid in what they can do, and if you want to customize anything you have to rewrite the whole thing. They effectively lock you into one black box of doing things, and getting out of it can be very painful.
Re:Rating 26 years of Java changes by 2muchcoffeeman on Saturday October 11, 2025 @07:24PM (#45553641)
Spring boot just provides what they think are reasonable defaults and you provide some specifics.

You can always inject your own implementation if needed right?

Re:Rating 26 years of Java changes by jsight on Saturday October 11, 2025 @09:53PM (#45554446)
In theory, yes. In practice, I've found that things get really complicated as soon as you start trying to interact with the spring lifecycle. Figuring out how to customize things and manage priority is the trickiest thing in Spring, IMO.
Re:Rating 26 years of Java changes by lenkite on Sunday October 12, 2025 @03:40AM (#45556138)
Please use the facilities of the framework for debugging any lifecycle issues. I was surprised when I found out that people did not use `--debug` for example or enabled logging of application startup events.

If you prefer GUI, Intellij even has a Spring Debugger: https://www.jetbrains.com/help/idea/spring-debugger.html

Re:Rating 26 years of Java changes by Anonyneko on Sunday October 12, 2025 @04:05PM (#45561383)
I stopped using Spring Debugger because for some inexplicable reason enabling it makes breaking at the breakpoint take minutes instead of seconds. For quite a while I didn't realize this was the problem, as IntelliJ started having it turned on by default at some point.
Re:Rating 26 years of Java changes by jsight on Sunday October 12, 2025 @09:59PM (#45563997)
Fair point, honestly. It could easily be a skill issue from not doing this particular type of thing all that often.
Re:Rating 26 years of Java changes by arcbyte on Sunday October 12, 2025 @10:01AM (#45558282)
Spring is written by a committee of junior developers trying to implement the ideas from a committee of slightly less junior developers. Which is to say it's a huge unintelligible mess. As the other commenter attest, it works really well if you need the one thing it does - unfortunately the moment you need something slightly different you are forced to dig deep into the insane internals and it is a disaster. This moment comes very quickly too in every project because it was written by people with no experience in real projects.
Re:Rating 26 years of Java changes by dhosek on Saturday October 11, 2025 @07:29PM (#45553684)
Oh, I think it’s quite wonderful really. There are cases where the limited nature of some configuration-based things ends up being a mess (one that comes to mind is a feature in Spring Data where you can extend a DAO bean into a rest service through annotations, but it turns out that this feature (at least when I last tried working with it), is so rigid as to be nearly useless in actual practice. But our codeless application was a bit of brilliance, I think.
Re:Rating 26 years of Java changes by bdangubic on Saturday October 11, 2025 @05:02PM (#45552688)
this is exactly why spring succeeded. I need to run a scheduled job, @EnableScheduling then @Scheduled(cron = “xxxxxx”) - done. I need XYZ, @EnableXYZ the @XYZ… sh*t just works…
Re:Rating 26 years of Java changes by taftster on Saturday October 11, 2025 @05:59PM (#45553053)
And then I realize I need to change that schedule. And would like to do it without recompiling my code. Oh, and I need to allow for environment specific scheduling, weekdays on one system, weekends on others. And I need other dependencies that are environment specific.

I much prefer Spring's XML configuration from the old days. Yeah, XML sucks and all that. But still, with XML, the configuration is completely external from the application and I can manage it from /etc style layouts. Hard coding and compiling in dependency injection via annotations or other such behaviors into the class directly has caused me grief over the long term pretty much every time.

Re:Rating 26 years of Java changes by nunobrito on Saturday October 11, 2025 @06:29PM (#45553206)
The only real-world usage I see for annotations are in GSON (the @Expose) and JUnit with @Test.

Never really came across with any other real cases where it solves a pressing issue as you mention. Most times is far more convenient to do things outside the compiled code.

Re:Rating 26 years of Java changes by taftster on Saturday October 11, 2025 @10:28PM (#45554632)
Agreed. These are good examples where annotations seem like a good fit. Being able to tell a processor that a method or field is "special". Test methods, serializable hints, etc.

It's kind of like, when annotations were delivered to Java, lots of projects thought they were just the next greatest thing. I followed right along at the time, as well.

But over time, finding the value in the configuration-as-code approach to application deployment, I definitely feel that annotations have been abused for many use cases.

Re:Rating 26 years of Java changes by bdangubic on Saturday October 11, 2025 @07:42PM (#45553778)
all trivial things you are listing, every single one…
Re:Rating 26 years of Java changes by sass_muffin on Saturday October 11, 2025 @08:21PM (#45553991)
I do realize you were intending to give examples of why you don't think annotations aren't very extensible, but it is an odd example as all those things can still be achieved via annotation, since the annotations can accept values loaded from env specific properties.
Re:Rating 26 years of Java changes by vlovich123 on Saturday October 11, 2025 @10:17PM (#45554570)
> env specific properties

And then if you want to change a value at runtime you have to restart the executable?

Re:Rating 26 years of Java changes by petersellers on Saturday October 11, 2025 @10:26PM (#45554620)
Most web application servers work this way. It also works really well in practice using modern CD tools - update your configuration and perform a gradual rollout of all your application servers to reflect the updated configuration.
Re:Rating 26 years of Java changes by bdangubic on Saturday October 11, 2025 @10:51PM (#45554745)
people will argue crazy sht here on HN like changing schedule is a thing* that needs instant gratification and god forbid you have to bounce a service to read an updated configuration … :)
Re:Rating 26 years of Java changes by happymellon on Monday October 13, 2025 @02:58AM (#45565518)
I have to deal with this at work for rolling db credentials, actuator refreshes are fine for this purpose.

Would be nicer if we could handle creds like it wasn't 1992, but this does the job too.

Re:Rating 26 years of Java changes by le-mark on Saturday October 11, 2025 @08:52PM (#45554129)
Exactly this, it’s great fun to have a surface level understanding of a topic and post derisively for internet points; rather then spend the time and effort to actually learn about the subject at hand!
Re:Rating 26 years of Java changes by taftster on Saturday October 11, 2025 @10:02PM (#45554505)
I'm not digging for "internet points". Yes, superficial replacement values can be retrieved from the environment. But I guess we have to give you a more imagined or sophisticated example then to make the point to you?

How about varying implementations of a service interface. Let's say I have a Scheduler interface and I want to have multiple implementations; maybe one is CronScheduler, another is RandomScheduler, another is BlueMoonScheduler. Each of these schedulers have their own properties and configuration values. I might want to choose, per environment or deployment context, which service implementation to use.

Annotation configuration makes this (near?) impossible to dynamically wire and configure these scenarios and make them tailored to the environment or deployment scenario. Annotations are generally "static" and do not follow a configuration-as-code approach to application deployment.

An external configuration file, as offered by Spring's original (less "favored") XML style, allowed a more composable and sophisticated configuration-as-code approach. Maybe that's a negative, putting that much power into the hands of the service administrator. But as I stated originally, in my experience, having statically defined annotation driven configuration and/or dependency injection has caused more problems than it has solved.

Re:Rating 26 years of Java changes by bdangubic on Saturday October 11, 2025 @10:53PM (#45554758)
Annotation configuration makes this (near?) impossible to dynamically wire and configure these scenarios and make them tailored to the environment or deployment scenario

all of your scenarios are trivial to implement with annotations

Re:Rating 26 years of Java changes by tpmoney on Sunday October 12, 2025 @11:06PM (#45564339)
> Annotation configuration makes this (near?) impossible to dynamically wire and configure these scenarios and make them tailored to the environment or deployment scenario. Annotations are generally "static" and do not follow a configuration-as-code approach to application deployment.

Off the top of my head, you could drop a `@Profile` or `@ConditionalOnProperty` annotation on each of your different schedulers and pick them at launch time simply by adding the profile you want to the arguments or the environment. That assumes you want to choose one for the whole app. If you want to have different ones in different locations, you can dynamically load beans in code. Or if you want them loaded entirely with annotations, you could define differently named beans for each context, and include `@Qualifier`s on them and in the locations they're being used.

Which isn't to say that annotations are perfect, but dynamic runtime configuration is sort of core to how spring operates, and annotations are no exception to that.

Re:Rating 26 years of Java changes by paulddraper on Saturday October 11, 2025 @08:55PM (#45554148)
So…pass that variable to your annotation.

I’m not seeing the point?

Re:Rating 26 years of Java changes by happymellon on Monday October 13, 2025 @02:54AM (#45565489)
You do know about environmental variables?
Re:Rating 26 years of Java changes by vbezhenar on Saturday October 11, 2025 @05:10PM (#45552743)
Yeah, it works until it isn't. And they good luck debugging it. I'd prefer simple obvious linear code calling some functions over this declarative magic any day.

    cronService.schedule("xxx", this::refresh);
This isn't any harder than annotation. But you can ctrl+click on schedule implementation and below easily. You can put breakpoint and whatnot.
Re:Rating 26 years of Java changes by bdangubic on Saturday October 11, 2025 @05:44PM (#45552965)
never had any issues debugging as I am never debugging the scheduler (that works :) ) but my own code.

and what exactly is “cronService”? you write in each service or copy/paste each time you need it?

Re:Rating 26 years of Java changes by alex_smart on Saturday October 11, 2025 @06:18PM (#45553146)
You can write your own libraries?

My goodness. What a question!

Re:Rating 26 years of Java changes by bdangubic on Saturday October 11, 2025 @07:43PM (#45553782)
you write your own database driver? encryption?
Re:Rating 26 years of Java changes by blackoil on Sunday October 12, 2025 @02:09AM (#45555691)
If you want to build an application from scratch, you must first create the universe.
Re:Rating 26 years of Java changes by alex_smart on Sunday October 12, 2025 @02:22AM (#45555766)
All this because I told one guy who asked "what exactly is “cronService”? you write in each service or copy/paste each time you need it?" that they can reuse code by writing a library instead of copy/pasting it?

If this is the level of incompetence encouraged by a framework, I would avoid using it just to avoid the chance of hiring people like you.

Just kidding. Spring boot is great. But yeah, I would fire people with this attitude without blinking an eye.

Re:Rating 26 years of Java changes by bdangubic on Sunday October 12, 2025 @07:56AM (#45557545)
* Why would I write my own database driver or encryption just because I wanted to implement my own "cronService"?*

how do you decide whether you will write your own or pull in a dependency? this is a legit question. you did start this with writing your own “cronService” (which is about as insane as writing your own database driver) so asked about it.

Re:Rating 26 years of Java changes by alex_smart on Sunday October 12, 2025 @12:19PM (#45559338)
> you did start this with writing your own

I really did not. I only said that if you were to create your own cronService, you can reuse it by creating your library rather than copy pasting code (which is obviously insane).

> which is about as insane as writing your own database driver

No, it is not. Spring Boot’s support for async jobs and scheduled jobs is lacking. A lot of people roll their own. Including yours truly.

It is also much easier than writing a database driver so there is that.

Re:Rating 26 years of Java changes by bdangubic on Sunday October 12, 2025 @04:41PM (#45561702)
Spring Boot’s support for async jobs and scheduled jobs is lacking.

Can you elaborate? What exactly is lacking and what version of Spring are you using?!

Re:Rating 26 years of Java changes by alex_smart on Monday October 13, 2025 @04:40AM (#45566166)
Compare with the functionality offered by async job systems of other full stack frameworks - eg django with celery and rails with solid-queue. It’s not even close.

I am on the latest version of Spring Boot.

Re:Rating 26 years of Java changes by bdangubic on Monday October 13, 2025 @09:32AM (#45568156)
I am not saying there aren't more robust scheduling libraries, people still use Quartz a lot in the Java ecosystem - was just wondering what specifically are you up against that you cannot solve with Spring's scheduling?
Re:Rating 26 years of Java changes by alex_smart on Sunday October 12, 2025 @02:12AM (#45555705)
Why would I write my own database driver or encryption just because I wanted to implement my own "cronService"?
Re:Rating 26 years of Java changes by throwaway7783 on Saturday October 11, 2025 @09:20PM (#45554271)
Whole point of spring is so you don't have to write your own libraries. Batteries included and all.
Re:Rating 26 years of Java changes by bdangubic on Saturday October 11, 2025 @09:47PM (#45554415)
@alex_smart hand rolls it all :)
Re:Rating 26 years of Java changes by alex_smart on Sunday October 12, 2025 @02:16AM (#45555721)
What if I told you - you can use a batteries-included framework, and still write your own libraries specifically only for things you want your own version of and want to share across projects?

The problem isn't that I don't know how to use a batteries included framework. The problem is that you guys don't know there is even an option to reuse your code by writing libraries.

Re:Rating 26 years of Java changes by throwaway7783 on Sunday October 12, 2025 @11:18AM (#45558805)
Why are you assuming you cannot write your own library and use it with Spring? It's not an either/or, just like most frameworks are. You are framing it as if there is only one way to do it in spring.

Please do not project things like "you guys don't even know..". I'm one of "you guys" , and have built production code in a variety of languages and frameworks. So this "you guys" knows exactly what he/she is talking about.

Re:Rating 26 years of Java changes by alex_smart on Sunday October 12, 2025 @12:11PM (#45559269)
> Why are you assuming you cannot write your own library and use it with Spring?

I am not. I am literally saying the exact opposite.

Re:Rating 26 years of Java changes by throwaway7783 on Sunday October 12, 2025 @12:47PM (#45559609)
Okay, that's fair. I don't even know anymore what point you are trying to make.
Re:Rating 26 years of Java changes by alex_smart on Sunday October 12, 2025 @02:47PM (#45560671)
I was making exactly the point that you can use your own libraries along with a batteries included framework like Spring Boot.

I don't even understand what the source of confusion is. I literally said exactly the same thing in the comment you first you replied to.

Re:Rating 26 years of Java changes by vbezhenar on Sunday October 12, 2025 @05:42AM (#45556834)
`cronService` is an injected instance of some class, probably provided by framework. My point is to demonstrate alternative, imperative way of defining cron tasks, as compared to declarative one.
Re:Rating 26 years of Java changes by vips7L on Sunday October 12, 2025 @09:04AM (#45557967)
A better name would probably be CronScheduler or something else. “Service” is always overused and doesn’t actually describe or mean anything.
Re:Rating 26 years of Java changes by skeletal88 on Saturday October 11, 2025 @05:23PM (#45552828)
Then you need to deploy it on multiple nodes and neex to make sure it only runs once for each run of the cron, etc.
Re:Rating 26 years of Java changes by bdangubic on Saturday October 11, 2025 @05:41PM (#45552953)
while not working on out of the box clustered this is trivial issue to address
Re:Rating 26 years of Java changes by didntcheck on Saturday October 11, 2025 @09:29PM (#45554322)
I believe Quartz is the go-to solution for this. It's not part of Spring but it offers a similar annotation-driven interface, but with distributed locking via a database
Re:Rating 26 years of Java changes by bdangubic on Sunday October 12, 2025 @04:43PM (#45561713)
Absolutely! But Quartz is also quite heavy. If all you need is to ensure scheduled jobs run in a clustered environment there are more “lighweight” options
Re:Rating 26 years of Java changes by happymellon on Monday October 13, 2025 @03:02AM (#45565550)
Like Shedlock?
Re:Rating 26 years of Java changes by mikelpr on Wednesday October 15, 2025 @04:18PM (#45597870)
why does this sound like systemd?
Re:Rating 26 years of Java changes by tormeh on Saturday October 11, 2025 @04:45PM (#45552533)
It's seriously puzzling. I just don't get how it's possible to look at what so many others have done better, and somehow design something worse. For what reason? Consistency with the rest of the language, possibly? But is that really so important. Do they just not want to tackle certain parts of the compiler?
Re:Rating 26 years of Java changes by rzwitserloot on Saturday October 11, 2025 @05:21PM (#45552812)
OpenJDK redesigns massive swaths of the compiler every other month.

The true explanation, at least the way OpenJDK says it, is that designing language features is more complex than a casual glancer can fathom, and there's 30 years of "Java is in the top 5 most used languages on the planet, probably #1 especially if focussing on stuff that was meant to be supported for a long time" to think about.

From personal experience, essentially every single last "Just do X to add (some lang feature) to java; languages A and B do it and it works great!" would have been bad for java. Usually because it would cause a 'cultural split' - where you can tell some highly used library in the ecosystem was clearly designed before the feature's introduction.

Even if you introduce a new feature in a way that doesn't break existing code, it's still going to cause maintainability headaches if you've cornered the pillars of the ecosystem into total rewrites if they want to remain up to date with the language. Because they will (or somebody will write an alternative that will) and you've _still_ 'python 2 v python 3'd the language and split the baby in the half.

For what its worth, I think the OpenJDK team doesn't take this seriously enough, and a number of recently introduced features have been deployed too hastily without thinking this through. For example, `LocalDate`, which has 'this should be a record' written all over it, is not a record. Or how the securitymanager is being ditched without replacements for what it is most commonly used for here in the 2020s. (To be clear: Ditching it is a good idea, but having no in-process replacement for "let me stop attempts to access files and shut down the JVM, not for security purposes but simply for 'plan B' style fallback purposes" - that's a bit regrettable).

I'm nitpicking on those points because on the whole OpenJDK is doing a far better job than most languages on trying to keep its ecosystem and sizable existing codebase on board _without_ resorting to the crutch of: "Well, users of this language, get to work refactoring everything or embrace obsoletion".

Re:Rating 26 years of Java changes by halffullbrain on Saturday October 11, 2025 @07:51PM (#45553824)
But ... LocalDate predated records by 6 years?

Eventually, I guess there'll be backwards compatible "pattern extractors" functionality retrofittable to existing "record-like" classes. This has been hinted at on several occasions.

Re:Rating 26 years of Java changes by MBCook on Saturday October 11, 2025 @08:14PM (#45553960)
Records are great, but objects work.

Date flat out doesn’t. We needed something in the standard library to fix that. It should’ve happened long before it did.

Re:Rating 26 years of Java changes by halffullbrain on Sunday October 12, 2025 @08:12AM (#45557639)
Totally agree, I was just pointing out to GP why LocalDate wasn’t a record. (Date and Calendar should never be used in new code. Note how the new date/time API doesn’t have any conversions to/from old types (Date+Calendar), they only in the opposite direction)

Can’t wait for destructuring support for classes, though.

Re:Rating 26 years of Java changes by MBCook on Sunday October 12, 2025 @10:24AM (#45558443)
Yes! Ever since I learned about it and was able to use it in some other languages I’ve been waiting for destructuring in Java to hit prime time.
Re:Rating 26 years of Java changes by rzwitserloot on Monday October 13, 2025 @09:12AM (#45567960)
> But ... LocalDate predated records by 6 years?

Yes, exactly - now you're getting it. Or rather I get the feeling I failed to explain it well.

ArrayList predates generics.

However, ArrayList does have generics.

That's because generics were added to the language in a 'culturally backwards compatible' way: Existing libraries (i.e. libraries that predate the introduction of generics, such as ArrayList) could modify themselves to add support for generics in a way that is backwards compatible for the library: Code written before they added it continues to work and compile fine even against the new release that added generics.

The same principle applied to records would mean that LocalDate could have been updated to turn into a record in a way that is backwards compatible.

And it really works that way.. almost. You really can take an existing class (defined with `class`) and change it into a record (defined with `record`) and all existing code continues to work just fine. However, this means all your properties now neccessarily get an accessor function that is named after the property. And that is a problem for LocalDate specifically: It already has accessors and they are named e.g. `getYear()`. Not `year()`. That means if LocalDate were to be rewritten as a record, one of two very nasty options must be chosen:

* Break backwards compatibility: As part of upgrading code you must change all calls to `.getYear()` into calls to `.year()`. It's a total ecosystem split: Every dependency you use comes in 2 flavours, one with calls to getYear and one with year, and you must use the right ones. This is truly catastrophic.

* Have both methods. There's year() and also getYear() and they do the same thing. Which is the lesser evil by far, but it makes very clear that LocalDate predates `record`. Contrast to ArrayList: It is not all that obvious that ArrayList predates generics. It does, but if you were to design ArrayList from scratch after generics are introduced you'd probably make the same code. Maybe the signature of `remove` would have been `remove(T)` instead of the current `remove(Object)`.

Instead, obviously then, the best choice is to not make it a record. And that's my point: The best possible (possibly here perfection is the enemy of good, but, I'd have done it differently) way to deploy records would have included some way for localdate to turn into a record without the above dilemma.

Perhaps simply a way to explicitly write your accessors with some marker to indicate 'dont generate the default `year()` - THIS is the accessor for it'.

Had that feature been part of record, then LocalDate could have turned into one in a way that you can't really tell.

Re:Rating 26 years of Java changes by dcminter on Saturday October 11, 2025 @04:53PM (#45552606)
Some of the weirder choices have been the result of a desire to avoid making breaking changes to JVM bytecode.

Also there was a long period when changes were very lumpy - it could be multiple years for a feature to make it into the release, and anything that might screw up other features got a lot of pushback. Then other conventions/tools emerged that reduced the urgency (e.g. the Lombok stuff)

Edit: I should add that it's now on a fixed 6-monthly release cycle which IMO works much better.

Re:Rating 26 years of Java changes by vbezhenar on Saturday October 11, 2025 @04:57PM (#45552647)
I don't really feel that Java uses proven features.

For example they used checked exceptions. Those definitely do not seem like proven feature. C++ has unchecked exceptions. Almost every other popular language has unchecked exceptions. Java went with checked exceptions and nowadays they are almost universally ignored by developers. I'd say that's a total failure.

Streams another good example. Making functional API for collections is pretty trivial. But they decided to design streams for some kind of very easy parallelisation. This led to extremely complicated implementation, absurdly complicated. And I've yet to encounter a single use-case for this feature. So for very rare feature they complicated the design immensely.

Modules... LoL.

We will see how green threads will work. Most languages adopt much simpler async/await approach. Very few languages implement green threads.

Re:Rating 26 years of Java changes by rzwitserloot on Saturday October 11, 2025 @05:25PM (#45552847)
> For example they used checked exceptions.

Those are from java 1.0 and thus don't appear to be relevant to the part of the discussion I think this part of the thread is about (namely: "Why doesn't java crib well designed features from other languages?").

> Java went with checked exceptions and nowadays they are almost universally ignored by developers.

They aren't.

Note that other languages invented for example 'Either' which is a different take on the same principle, namely: Explicit mention of all somewhat expectable alternative exit conditions + enforcing callers to deal with them, though also offering a relatively easy way to just throw that responsibility up the call chain.

The general tenet (lets lift plausible alternate exit conditions into the type system) is being done left and right.

Re:Rating 26 years of Java changes by jayd16 on Saturday October 11, 2025 @07:06PM (#45553497)
I suppose you could actually solve it by having a promise that catches the exception like your suggesting with an either result. The C# Task API can do this. It has it's own headaches where now the developer has to pay attention to observing every exception.

Java could do something similar but they have enough promise types already.

Re:Rating 26 years of Java changes by vips7L on Sunday October 12, 2025 @09:16AM (#45558040)
All modern languages are adopting a checked error system: Rust, Swift, Kotlin, Zig, Gleam; they all have some type of error you must handle.

The problem with Java is that they haven’t added the syntax to make dealing with those errors easy. It’s boiler plate hell.

Re:Rating 26 years of Java changes by rectang on Sunday October 12, 2025 @12:46PM (#45559604)
Yeah, unwrapping a Result in Rust can often be done via a single character, the chainable `?` operator.

That’s not the only issue, though: Java also shunts both checked and unchecked exceptions through the same mechanism, conflating them. It’s no wonder that Java’s problematic implementation of checked exceptions has poisoned people against the concept.

Re:Rating 26 years of Java changes by dkarl on Saturday October 11, 2025 @07:02PM (#45553455)
You're absolutely right about checked exceptions. However, I think they're an exception (forgive me) from the pattern of Java mostly sticking to the strategy of, we can build a practical, industrial, reasonably performant language that has all these nice bits from other languages: garbage collection, portable bytecode, no pointer arithmetic, collections in the standard library, etc.

I think streams are a great example of what I was saying about Java failing to take advantage of coming last. Scala (probably among others, but Scala was right there on the JVM) had already demonstrated that it was possible to enable simple, readable code for simple use cases, while also enabling complex and powerful usage. And the experience of Scala had shown that there's little demand for parallel collections outside of extremely niche use cases where people tend to use specialized solutions anyway. Somehow Java, with this example staring them in the face, managed to get the worst of both worlds.

Re:Rating 26 years of Java changes by lenkite on Sunday October 12, 2025 @03:35AM (#45556108)
Totally Hard disagree on streams - I used parallel streams in my last Job nearly all the time. They are critical for cpu-intensive tasks involving large datasets. And they do involve just a single code change in the consuming code. Sequential to parallel processing can be done via one `.parallel`.

I always believed it was a major plus point for Java compared to other languages. I am even surprised to hear otherwise. How should parallel processing of streams work in your opinion, then ? Just saying it be unsupported would be laughable considering hardware today.

I would rate this feature 9/10. The fact that the author has rated it 1/10, shows he hasn't really worked on large, parallel processing of data - in Java anyways.

Re:Rating 26 years of Java changes by dkarl on Sunday October 12, 2025 @09:56AM (#45558250)
In the companies I've worked at, those kinds of workloads have been done in Spark or in Beam (GCP Dataflow, etc.)
Re:Rating 26 years of Java changes by JavierFlores09 on Saturday October 11, 2025 @06:02PM (#45553063)
Stuart Marks and Nicolai Parlog recently had a discussion about checked exceptions in the Java channel [0]. In short, while they mentioned that there are certainly some things to improve about checked exceptions, like the confusing hierarchy as well as the boilerplate-y way of handling them, they're not necessarily a failed concept. I do hope they get to work on them in the near future.

0: https://www.youtube.com/watch?v=lnfnF7otEnk

Re:Rating 26 years of Java changes by vbezhenar on Saturday October 11, 2025 @06:26PM (#45553189)
They are absolutely failed concept in Java. Every first popular library uses unchecked exceptions, including famous Spring. Java streams API does not support checked exceptions. Even Java standard library nowadays includes "UncheckedIOException". Kotlin, Scala: both languages grown from JVM and do not support checked exceptions.
Re:Rating 26 years of Java changes by samus on Sunday October 12, 2025 @02:21AM (#45555754)
Spring has a special error handling strategy where the whole request is allowed to fail, punting error handling off to the caller.

A lot of code that throws checked exceptions is simply dangerous to use with Java streams because the execution order of stream operation is not obvious and possibly non-deterministic. For this reason, streams were never intended to also handle errors. Reactive frameworks are much better at that.

The UncheckedIOException is for situations where you really cannot throw a checked exceptions, such as inside an iterator. Which might lead to ugly surprises for the API user;

Re:Rating 26 years of Java changes by vbezhenar on Sunday October 12, 2025 @05:48AM (#45556856)
> A lot of code that throws checked exceptions is simply dangerous to use with Java streams because the execution order of stream operation is not obvious and possibly non-deterministic.

From the type system PoV, they could have just written something like `interface Runnable<X> { void run() throws X; }` and now `forEach` would have been written like `<X> void forEach(Runnable<X> r) throws X`. And repeat that for all stream operations, promoting `X` everywhere, so if your mapper function throws SQLException, the whole pipeline will throw it.

It even works today with some limitation (there's no way for `X` to get value of `SQLException|IOException` union type), but with some generic improvements it could work.

But they decided to not touch this issue at all. So now people will fool compiler with their tricks to throw checked exceptions like unchecked (or just wrap them with UncheckedXxxException everywhere, I prefer that approach).

Re:Rating 26 years of Java changes by samus on Monday October 13, 2025 @07:54AM (#45567342)
At the risk of repeating myself: streams were simply never designed with error handling in mind. Aborting everything at the first error is just the most simplistic error handling strategy. Reactive streams frameworks should be preferred for this.
Re:Rating 26 years of Java changes by peterashford on Sunday October 12, 2025 @08:33PM (#45563488)
I agree. The only issue for me is that they could be less verbose but given that I'm always using an IDE, this is in practice a non-issue
Re:Rating 26 years of Java changes by bigstrat2003 on Saturday October 11, 2025 @08:54PM (#45554140)
> For example they used checked exceptions. Those definitely do not seem like proven feature.

Checked exceptions are an awesome feature that more languages should have. Just like static typing is a good thing because it prevents errors, checked exceptions are a good thing because they prevent errors.

Re:Rating 26 years of Java changes by rectang on Saturday October 11, 2025 @10:21PM (#45554589)
Idealized checked exceptions are isomorphic to Rust's `Result` type, which is great.

Java's implementation of checked exceptions has some issues, though.

* "Invisible control flow", where you can't tell from the call site whether or not a call might throw (you need to check the signature, which is off in some other file, or perhaps visible in an IDE if you hover).

* Java has both checked and unchecked exceptions, but they go through the same try-catch mechanism, failing to make a clean distinction between recoverable errors and unrecoverable bugs. (In e.g. Rust and Go, recoverable errors go through return values but unrecoverable errors go through panics.)

In the end, Java's exception design simultaneously requires a lot of effort to comply with, but makes it difficult to understand when you've successfully locked things down.

Re:Rating 26 years of Java changes by samus on Sunday October 12, 2025 @02:03AM (#45555666)
> * "Invisible control flow", where you can't tell from the call site whether or not a call might throw (you need to check the signature, which is off in some other file, or perhaps visible in an IDE if you hover).

Never found this this to be a problem. It is really common to all implementations of exceptions, not just checked ones. And when you write code the compiler will yell at you. In monadic code,

Re:Rating 26 years of Java changes by rectang on Sunday October 12, 2025 @11:47AM (#45559062)
Invisible control flow is common to all implementations of unchecked exceptions (Java, C#, C++, Python, Ruby, etc). It means that any code, anywhere, at any time, can throw an exception.

People are used to that, and one common strategy is to not worry too much about exceptions and then wrap a big `try` block around everything near the outer boundary of your code. It’s good enough for many purposes and yields a high initial development velocity, but is comparatively fragile.

With Languages like Rust, Go, and Swift, only unrecoverable errors trigger the panic mechanism. Every call site where a recoverable error may occur is identifiable — in Rust via Result, `unwrap()`, the `?` operator, etc, in Go via returned Err (though unlike Rust you can discard them silently), and in Swift via the `try` operator.

You can still develop quickly by just unwrapping every Result, but unlike languages with invisible control flow, you can easily audit the codebase and go back to harden every site where a recoverable error may occur — yielding a level of robustness which is difficult to achieve in languages with unchecked exceptions.

Re:Rating 26 years of Java changes by peterashford on Sunday October 12, 2025 @08:32PM (#45563480)
As someone who uses Go professionally now and Java previously, I disagree with your take on unchecked exceptions. I think that Panics and errors is worse than just using exceptions. I think the latter is just simpler to deal with. At the end of the day, when error handling is complex, its complex with either approach - you have to think carefully about what's happening in the problem domain and come up with a robust solution. What the code level adds is complexity: the Go approach is more complex and consequently worse. I love Go coding but its error handling sucks and I would gladly have the Java approach instead.
Re:Rating 26 years of Java changes by vips7L on Sunday October 12, 2025 @09:21AM (#45558064)
Do you not have to check the signature to see what a function can return when using Results? It’s off in another file too.

> failing to make a clean distinction between recoverable errors and unrecoverable bugs

Recoverability is context specific. One persons panic may just be another error case for someone else. I think this is one thing that programmers miss when talking about this topic. It is really up to the caller of your function if something should panic. You can’t make that decision for them.

Re:Rating 26 years of Java changes by rectang on Sunday October 12, 2025 @12:06PM (#45559225)
The point of avoiding invisible control flow is not to identify the type of the error, but to identify all locations in the code where recoverable errors may occur.

> One persons panic may just be another error case for someone else.

We can make a strong distinction between recoverable errors (e.g. this I/O operation may fail) versus unrecoverable errors that may leave the process in an unsound state, such as divide-by-zero or out-of-bounds array access[1].

There are some problem domains where even unrecoverable errors are not allowable, and programmers in those domains have to grapple with the panic mechanism.

But for the rest of us, it is useful to be able to distinguish between recoverable and unrecoverable errors — and to know how we have handled all possible sites which could result in recoverable errors.

[1] Joe Duffy explains it well: https://joeduffyblog.com/2016/02/07/the-error-model/#bugs-ar...

Re:Rating 26 years of Java changes by paulddraper on Saturday October 11, 2025 @08:57PM (#45554154)
Go implements green threads.

It might only be one language, but it’s a pretty big one

Re:Rating 26 years of Java changes by jsight on Saturday October 11, 2025 @09:57PM (#45554474)
I agree with you w/r/t the streaming parallelization. I remember huge arguments about this on some of the lists back in the day, because that design decision had lots of ridiculous consequences.

Eg, mutable state capture in lambdas is largely restricted because of the thought that people would use parallel threads within the stream processing blocks. That decision lead to lots of ugly code, IMO.

I've also never seen a need to try to parallelize a simple stream processing step.

Re:Rating 26 years of Java changes by peterashford on Sunday October 12, 2025 @08:35PM (#45563507)
I've used Streams before to good effect but I can't say I'm in love with the design. Seems overly verbose.
Re:Rating 26 years of Java changes by stirfish on Sunday October 12, 2025 @12:50AM (#45555324)
You've never used parallel streams? They're my favorite way to do parallel computation in Java, and very easy if you've structured your problem around streams.
Re:Rating 26 years of Java changes by vbezhenar on Monday October 13, 2025 @05:12AM (#45566357)
Code with parallel streams wouldn't even pass my review. The server processes multiple requests simultaneously. It makes no sense to smash all cores in one request. It'll cause bad latency for other requests and will not increase throughput.

There might be use-cases, but I've yet to encounter them.

And when I need parallel computation, I can just use good old ExecutorService. Few more lines, but that's OK for a task that arises once in a 10 years.

Re:Rating 26 years of Java changes by samus on Sunday October 12, 2025 @01:56AM (#45555643)
Checked exceptions are for errors that are not possible to prevent. How else should the caller know which exceptions are really likely to happen?

Modules absolutely achieved their primary goal: stopping libraries from accessing JDK internals without the application's knowledge. The ecosystem is slow on the uptake since split packages and access to internal APIs is endemic, but it is happening ever so slowly. I wish libraries could opt into not being part of the unnamed module.

Virtual threads were designed with explicit cooperation of the community, with the explicit goal of making it easy to switch as much existing code over to it as possible. I really don't understand the scepticism there. Most other languages went with promises or reactive streams because they were inspired by how functional programming languages do it.

Re:Rating 26 years of Java changes by vbezhenar on Sunday October 12, 2025 @06:02AM (#45556925)
> Checked exceptions are for errors that are not possible to prevent. How else should the caller know which exceptions are really likely to happen?

The same way, caller can know which exceptions are really likely to happen in TypeScript, C++, Python. Or in modern Java which avoids checked exceptions anyway. By reading documentation or source code. That's perfectly fine and works for everyone.

And you highlighted one big issue with checked exceptions. You've claimed that those exceptions are "really likely to happen".

When I'm writing reading data from resource stream, the data that's located next to my class files, IO Exceptions are really unlikely to happen.

Another ridiculous example of this checked exception madness:

    var inputStream = new ByteArrayInputStream(bytes);
    var outputStream = new ByteArrayOutputStream();
    inputStream.transferTo(outputStream); // throws IOException? wtf???
This code can throw OutOfMemoryError, StackoverflowError, but never IOException. Yet you're forced to handle IOException which doesn't happen. And that's the issue with checked exceptions.

There's no correspondence between checked exceptions and likelihood of their occurence. NullPointerException probably happens more than any checked exception. The division between checked exceptions and unchecked exceptions is absolutely arbitrary and makes sense only at caller place, never in called function signature.

> Modules absolutely achieved their primary goal: stopping libraries from accessing JDK internals without the application's knowledge. The ecosystem is slow on the uptake since split packages and access to internal APIs is endemic, but it is happening ever so slowly. I wish libraries could opt into not being part of the unnamed module.

"Slow" is an understatement. I don't see this happening at all. Last time I tried to write very simple application with modules, I spent so many hours banging my head over various walls, that I probably will not do another attempt in a foreseeable future.

Re:Rating 26 years of Java changes by clanky on Sunday October 12, 2025 @09:51AM (#45558229)
There's a recent Reddit thread with some good discussion around modules where Ron Pressler took part. tl;dr: the architects acknowledge uptake outside the JDK has been slow, largely because there have always been few benefits to modules, and secondarily because build tool support is lacking (perhaps because of the first reason). At some point they may begin providing additional benefits to modularization which may help stoke demand.

https://www.reddit.com/r/java/comments/1o37hlj/reopening_the...

Re:Rating 26 years of Java changes by samus on Sunday October 12, 2025 @04:10PM (#45561441)
[delayed]
Re:Rating 26 years of Java changes by vbezhenar on Sunday October 12, 2025 @06:09PM (#45562439)
> This provides no automatic verification that indeed all likely error situation that can and should be handled were indeed handled.

And some people write code in Python which provides no automatic verification whatsoever.

Actually unchecked exceptions are very similar to dynamically typed languages. And that's fine. As Python and other languages proved by their mere existence: dynamic typing is not inherently bad. Sometimes it's good. I think that for error handling, it's good.

> Java class loaders can do anything including loading resources from the network. Which is admittedly not that common these days after the demise of applets.

Technically they can, but in my particular case I know very well, that my resources are residing inside of JAR file. And if that JAR file happened to reside on the failed HDD block, that's not going to be a recoverable error.

When we're talking about IO Exceptions, it's almost always failed operation which requires complete abort. It's either failed hardware, or, typically, disconnected client. Can't do much about it, other than clean up and proceed to the next client.

And the same could be said about SQL Exceptions. Like 99% of SQL exceptions are application bugs which are not going to be handled in any way other than wind up and return high level HTTP 500 error or something like that. There are cases when SQL exception should be handled, but those are rare. Yet JDBC developers decided that programmers must execute error handling rituals on every call site.

> The NullPointerException indicates a bug in the application. By the very reason it occurs, the current thread cannot continue execution normally.

That's not exactly true. In JVM, NullPointerException is absolutely well defined and you can continue execution after catching it. You might suspect, that logical application state is corrupted, but sometimes you know well that everything's fine (and in most cases you hope that everything's fine, if your Spring MVC handler threw NPE, Spring is not going to crash, it'll continue to serve requests). It's not C++ with its undefined stuff. JVM is pretty reliable when it comes to every error, including NPE, stack overflow or OOM. Latter is special, because even handling error might prove challenging, when memory allocations fail, but JVM will not hang up or crash.

Re:Rating 26 years of Java changes by samus on Monday October 13, 2025 @07:47AM (#45567289)
> And some people write code in Python which provides no automatic verification whatsoever.

Python is a language with almost no static validation whatsoever. It would be very odd if it cared about checked exceptions. This dynamism makes big Python code bases infuriating to work with.

> When we're talking about IO Exceptions, it's almost always failed operation which requires complete abort. It's either failed hardware, or, typically, disconnected client. Can't do much about it, other than clean up and proceed to the next client.

If this is the case then the solution is to add it to the `throws` list.

> That's not exactly true. In JVM, NullPointerException is absolutely well defined and you can continue execution after catching it.

Why would I catch a NullPointerException instead of fixing my application? The JVM is indeed completely fine, but processing still cannot continue because that code simply does not exist.

Re:Rating 26 years of Java changes by vips7L on Sunday October 12, 2025 @09:11AM (#45558005)
Checked errors aren’t universally ignored by developers. Rusts main error system is checked. Swift has really introduced checked typed throws. Kotlin is introducing checked error unions. Checked exceptions are the same thing.
Re:Rating 26 years of Java changes by torginus on Saturday October 11, 2025 @05:13PM (#45552760)
which is kinda horrifying - it means the framework designers didn't find the language powerful enough to express app logic, and hotglued their own custom arbitrary behavior on top of it.

Clear language code should be endeavor to be readable/understandable when printed on a sheet of paper by anyone, acceptable code should be understandable by anyone who knows a bit about the technologies and has some IDE support.

Garbage code is what you have when the code in question is only understandable when you actually run it, as it uses arbitrary framework logic to wire things together based on metadata on the fly.

Re:Rating 26 years of Java changes by Groxx on Saturday October 11, 2025 @06:43PM (#45553291)
people have been gluing other languages on top of languages practically forever - it's a DSL.

no single language is ideally suited for every situation, it's not inherently a sign of failure that someone makes a DSL.

and since annotations are part of the language, this is still all "the language is flexible enough to build the framework [despite being wildly different than normal code]" so I don't think it even supports that part.

Re:Rating 26 years of Java changes by rzwitserloot on Saturday October 11, 2025 @05:15PM (#45552779)
That's not how the OpenJDK sees things. They tend to think that the features they deliver are at best mildly informed by other languages. Not out of some sense of hubris, but out of a sense of pragmatics: Simply copy and pasting features from other languages into java - that would produce a frankenstein.

For example, java is somewhat unique in having lambda syntax where the lambda *must* be compile-time interpretable as some sort of 'functional type' (a functional type being any interface that defines precisely 1 unimplemented method). The vast, vast majority of languages out there, including scala which runs on the JVM, instead create a type hierarchy that describe lambdas as functions, and may (in the case of scala for example) compile-time automatically 'box'/'cast' any expression of some functional type to a functional interface type that matches.

Java's approach is, in other words, unique (as far as I know).

There was an alternate proposal available at the time that would have done things more like other languages does them, completely worked out with proof of concept builds readily available (the 'BGGA proposal'). The JVM would autogenerate types such as `java.lang.function.Function2<A, B, R>` (representing a function that takes 2 arguments, first of type A second of type B, and returns a value of type R), would then treat e.g. the expression:

`(String a, List<Integer> b) -> 2.0;`

As a `Function2<String, List<Integer>, Double>`, and would also 'auto-box' this if needed, e.g. if passing that as the sole argument to a function:

``` void foo(MyOperation o) {}

interface MyOperation { Double whatever(String arg1, List<Integer> arg2); } ```

This proposal was seriously considered but rejected.

The core problem with your comment is this:

Define the terms "polished" and "elegant". It sounds so simple, but language features are trying to dance to quite a few extremely different tunes, and one person's 'elegance' is another person's 'frankensteinian monster'.

The same mostly goes for your terms "beauty" and "charm", but, if I may take a wild stab in the dark and assume that most folks have a very rough meeting of the minds as to whatever might be a "charming" language: I know of no mainstream long-term popular languages that qualify for those terms. And I think that's inherent. You can't be a mainstream language unless your language is extremely stable. When you're not just writing some cool new toy stuff in language X - you're writing production code that lots of euros and eyeballs are involved in, and there's real dependence on that software continuing to run, then you __must__ have stability or it becomes extremely pricey to actually maintain it.

With stability comes the handcuffs: You need to use the 'deprecation' hammer extremely sparingly, essentially never. And that has downstream effects: You can't really test new features either. So far I have not seen a language that truly flourishes on the crutches of some `from future import ...` system. That makes some sense: Either the entire ecosystem adopts the future feature and then breaking _that_ brings the same headaches, or folks don't use these features / only for toy stuff, and you don't get nearly the same amount of experience from its deployment.

Said differently: If java is a frankenstein, so is Javascript, C#, Python, Ruby, Scala, and so on. They have to be.

I'd love to see a language whose core design principles are 100% focussed on preventing specifically that. Some sort of extreme take on versioning of a language itself that we haven't seen before. I don't really know what it looks like, but I can't recall any language that put in the kind of effort I'd want to see here. This is just a tiny sliver of what it'd take:

* The language itself is versioned, and all previous versions continue to be part of the lang spec and continue to be maintained by future compilers. At least for a long time, if not forever.

* ALL sources files MUST start with an indication about which version of the language itself they use.

* The core libraries are also versioned, and separately. Newer versions are written against old language versions, or can be used by source on old language versions.

* The system's compilers and tools are fundamentally operating on a 'project' level granularity. You can't compile individual source files. Or if you can, it's because the spec explains how a temporary nameless project is implied by such an act.

* All versions ship with a migrator tool, which automatically 'updates' sources written for lang ver X to lang ver X+1, automatically applying anything that has a near-zero chance of causing issues, and guiding the programmer to explicitly fixing all deprecated usages of things where an automated update is not available.

* The language inherently supports 'facades'; a way for a library at version Y to expose the API it had at version X (X is older than Y), but using the data structures of Y, thus allowing interop between 2 codebases that both use this library, one at version X and one at version Y.

That language might manage the otherwise impossible job of being 'elegant', 'simple', 'mainstream', 'suitable for serious projects', and 'actually good'.

Re:Rating 26 years of Java changes by nine_k on Saturday October 11, 2025 @05:38PM (#45552931)
Absolutely. It seems that the author never touched Spring, for instance, or a dependency-injection framework of any kind. Annotations allow to do things in a completely different way, removing tons of boilerplate.

I'd give annotations 9/10 at least.

(And I lost the interest in the rest of the article, given such a level of familiarity with the subject matter.)

Re:Rating 26 years of Java changes by __float on Saturday October 11, 2025 @07:25PM (#45553648)
My experience using Dagger (2) was so unpleasant that it really soured me on the possible uses of this feature.

I understand the benefits of dependency injection, but to be totally honest I'm more likely to take the Go-style approach of wiring it all up manually, even if it's a bit of extra boilerplate. The indirection and abstractions built up in DI frameworks is rarely worth it IMO.

Re:Rating 26 years of Java changes by malfist on Saturday October 11, 2025 @07:31PM (#45553694)
Dagger is an absolutely pain in the ass. Its also damn good. Once you understand the archane syntax of their error messages its a lot easier (but still not easy) to use.

Harder than spring, but less magic than spring

Re:Rating 26 years of Java changes by vips7L on Sunday October 12, 2025 @09:07AM (#45557988)
Dagger is by far the worst UX experience for a DI framework. Really you should try anything else. For compile time DI like dagger you can try Avaje Inject:

https://avaje.io/inject/

Re:Rating 26 years of Java changes by Groxx on Saturday October 11, 2025 @06:37PM (#45553258)
and that's before even touching on the compilation steps they can add, which are a pluggable codegen and macro system that is also integrated into IDEs, which is completely missing from almost every other language.
Re:Rating 26 years of Java changes by jayd16 on Saturday October 11, 2025 @06:59PM (#45553426)
Good stuff. If that excites you, check out Roslyn source generators too.
Re:Rating 26 years of Java changes by nmadden on Sunday October 12, 2025 @03:49AM (#45556194)
Do you really think that in 26 years of professional Java programming I’d have never touched Spring? I’ve been using Spring since it was first released. I’ve found CVEs in Spring (https://spring.io/security/cve-2020-5408). Trust me when I say that my dislike for Spring (and annotations) is not based on ignorance.
Re:Rating 26 years of Java changes by ruszki on Sunday October 12, 2025 @10:09AM (#45558332)
But your dislike can be a lot of other things which are not objective the slightest sense. I mean, your whole article is a subjective piece. Also, stating about something that you “dislike” something as large as Spring as a whole is usually a huge red flag for anybody, just like how liking it without reservations is also a huge red flag.
Re:Rating 26 years of Java changes by nmadden on Sunday October 12, 2025 @11:51AM (#45559105)
Yes, of course it’s (largely) subjective. But I have actually read much of the source code of Spring. I know it _very_ well.
Re:Rating 26 years of Java changes by ruszki on Sunday October 12, 2025 @12:52PM (#45559662)
From my point of view, it seemed from you article that you didn't even really understand functional languages in two decades (at least Haskell), which it seemed you to try to state also. So that doesn't matter too much.

Anyway, I just wanted to say, that it's totally pointless to state something like "I know it well"... Say what's your problem with it, "I don't like it" doesn't add anything to the conversation. I'm quite sure whatever you would say as problems, most people would agree, maybe there would be even tips under it how to prevent it. That will never happen with the kind of comments which you made above.

Re:Rating 26 years of Java changes by nine_k on Sunday October 12, 2025 @11:15AM (#45558782)
It's perfectly possible to work for 26 years with Java and not ever seriously touch Spring, or AWT, or Swing, or the EE bits, etc. Java is sprawling, and a corporate backend developer and a mobile frontend developer may have little intersection in the big libraries, and even the approaches, they use.

It's perfectly fine to never have touched Spring. What surprised me is not acknowledging that not only are annotations used to do clerical things like @Override or @Deprecated, and not only to do some weird wiring with @Injected or @RequestBody, but allow to add large custom steps in transforming the code. Annotation processors are a huge comptime interface that can do, and routinely does, wild things, unimaginable in Go, the kind of code transformations you would expect in Lisp or Python.

I suspect the latter should have interesting security implications, too.

Re:Rating 26 years of Java changes by nmadden on Sunday October 12, 2025 @11:48AM (#45559070)
Java is sprawling now. It wasn’t 26 years ago.
Re:Rating 26 years of Java changes by nine_k on Sunday October 12, 2025 @01:03PM (#45559772)
Just 20 years ago it was already sprawling.
Re:Rating 26 years of Java changes by AtlasBarfed on Sunday October 12, 2025 @11:32AM (#45558945)
Spring was a great DI framework that I only use for DI.

All the big magic annotations are for Enterprise.

Okay, I've occasionally done a couple spring boot rest, which was ... Fine ... As long as you didn't have to do anything even remotely and complicated, but it keeps you in this weird box of middle performance.

If you've ever been on any large Enterprise spring Java project, you know what the future of vibe coded enterprise is bringing.

Re:Rating 26 years of Java changes by jayd16 on Saturday October 11, 2025 @06:53PM (#45553367)
The ratings are really all over the place. Jshell is a 6/10? What is the rubric?
Re:Rating 26 years of Java changes by jsight on Saturday October 11, 2025 @09:51PM (#45554436)
IDK, I've worked in projects that didn't use the magic so much and I honestly think it was worse.

Instead of a config class and a bunch of apps with @Inject Config config;, we'd have giant *Config classes. Each one would have lots of methods like:

@Bean public fooProducer(FooConfig config, BazProvider provider, BarProvider barProvider, SoapClient soapClient) {...}

Want to know how they were produced? Find usages on the class' constructor.

The magic @Inject and @Autowired annotations don't seem worse than that to me.

Re:Rating 26 years of Java changes by zeroq on Saturday October 11, 2025 @09:59PM (#45554491)
The list generally oversimplifies a lot of things, but you're on point with annotations.

Mandatory personal anecdote:

I'm not a java guy, but I've been around java since '99, and few years ago I was moved to a strictly java team. Upon introduction I decided to show them my party trick and implemented live a pseudo "wolf3d" in one day. As usual, java devs were sort of impressed by the fact that you can do graphics and user input in java, because nowadays that's extremely rare for most of them. I got my approval and in return I asked them to give me a quick one day deep dive into Spring.

At the end I was presented with a basic hello world project that was comprised mostly of... EMPTY CLASSES I mean literally, class Foo {} END OF FILE!

Of course these empty classes had at least 5 lines of annotations on top of class declaration and in the end it somehow pushed the framework into the right direction, but oh my fucking god, I was throwing up in my mouth for the rest of the week.