Hacker News new | ask | show | jobs
by moring 1101 days ago
The issues that I see with Spring aren't really code snippets from SO, but the fact that annotations drop all the advantages that Java had as a language. Just two examples:

When you create an Object of the wrong class and try to pass it to a method, you get a compile-time error. When you use the wrong annotation, nothing happens during startup of your application... but you don't even know at which point in time something should happen. Or if.

When a method you call throws an exception, you can run the application in a debugger and single-step into the method, then single-step until the exception gets thrown. This doesn't always just solve the problem but more often than not it gives you a good indication. If an exception gets thrown due to an annotation, you get an enormous stacktrace from some code you have never seen and didn't even know was run, or why it was run, from a thread you have never seen, complaining about wrong parameters that you have never seen, passed from another method you have never seen.

7 comments

Another one of my favorites. When you make an API for a library it's usually pretty clear what the user can do with it. When you make an annotation API for a spring library it's evidently impossible to expect how the user will use it. Random feature of a library won't work because the context you're using their annotation in aren't compatible with whatever assumptions they made.
These is just (another) reason for avoiding Spring. Whenever I'm tempted because of XY module that looks good, I'm reminded with feedback like yours that things are still the same.

Oh well, will continue using java far away from that framework.

I love java, and especially modern java, but I also love Ruby and python and the majority of my web dev has been in Rails.

I'm now working on a Java side project, and while typically in the past I've just done the backend in java and then used rails for the web UI and had them share a DB, I'm trying to see if I can use Java for the web part without going insane.

Part of the problem is that historically the big players in java web are HUGE enterprises that was to be able to have 50 teams all do a small part of a backend in parallel and then just deploy them all together. Thus was born the servlet API and application servers.

But there's so many assumptions and bizarre requirements that come out of trying to do this perverse form of engineering that the whole thing ends up nigh unusable for someone who could otherwise just "rails g" 85% of their project.

Jetty has always struck me as a bit of a middle man where if you want, you can do the servlet thing but it's also a production grade application framework that doesn't force you to do the java EE dance if you don't want to. Though there is some leakage. But it's a lot less magical than Spring and is also just the http server bits, not the rest of the db and view and etc.

But since virtual threads are pretty stable now, I really want to use them, and jetty is the first reasonably complete and robust option that seems to have included support for them.

So - you know how these things go. I'm currently writing an HTTP url path router that supports the rails syntax from routes.rb. And then I'm going to write a Handler implementation that wrangles all the database stuff and does convenient/terse parsing of params (like how rails folds path params, url params, and form params into a single params object) and rendering of responses (so I can render a json object without having to call Content.Sink.[...] and use gson all over the place.

It's meant to all be very non magical and you can step through the code in a debugger and not see a billion reflective invocations of methods. And I'm hoping I can make the API of my Handler convenient enough that you don't regret that it's not annotation magic-based.

Also - I know there are various attempts at easier/less annoying Java web things like vertx and such, but they a) don't support virtual threads yet, and b) many of them are small enough im worried they'll rot eventually. Jetty meanwhile isn't going anywhere.

> Also - I know there are various attempts at easier/less annoying Java web things like vertx and such, but they a) don't support virtual threads yet, and b) many of them are small enough im worried they'll rot eventually. Jetty meanwhile isn't going anywhere.

Vertx is arguably larger than Jetty or at least NOT smaller. Vertx is used by Quarkus and other Redhat/IBM projects e.g. if you use the newer Keycloak you're using Vertx. It's been around for a long time and has received constant updates.

Vertx is also 1 of the top performs of Techempower Benchmarks keeping Java in the race for those that care.

Sure, but they don't support virtual threads yet afaik.
What's the fixation on virtual threads?

Vertx performed better than a lot of frameworks without it - both in less memory and faster performance.

From my personal experience, the first example doesn't really happen: thankfully I rarely see people randomly throwing annotations at methods/classes hoping one of them will stick. For most of the time annotations are self-explanatory.

However, there are some real issues with annotations in Spring/Java: - Application will sometimes run just fine without annotation processor/interceptor. Think of `@EnableScheduling` in Spring: you won't know that `@Scheduled` is not working (because of missing `@EnableScheduling`) until you observe that method is not executed. In this case static code is a clear win. - Annotation order: not all annotation processors/interceptors in Spring support specifying order. Annotation order in the code doesn't matter: it is lost during compilation. Good luck figuring out what is applied first in a method with `@Retry`, `@Transactional` and `@Cached` - will retry be executed within transaction or each retry will have its own transaction? This also is easily solved with static code instead of annotations.

As for compile-time error vs runtime-error: personally I don't really care as long as there is any error (which is not always the case in the first example) during the build/test/init/assembly phase. When I'm writing SQL queries in the code, I'm getting SQL parsing/compilation errors during application runtime - but that's fine, because I've written SQL-s against DB execution engine. When I'm writing Spark SQL job, I'm getting errors during query planning phase - and that's also fine, because I'm writing code against Spark's execution engine. Writing annotations against "annotation execution engine" (annotation processor/interceptors) doesn't seem any different or wrong in principle. Although, there are things that could be improved.

Stacktraces: there are a few additional interceptor method calls in the stacktrace when annotations are in use, however, most of the complexity comes from library/framework structure and developer's familiarity with it. Spring covers a lot of use cases thus it has its share of complexity. I'm not sure if "Spring without annotations" would be noticeably easier to work with, although I assume that feature-parity with Spring (MVC) is not a goal of this project so it probably will be easier to understand.

> From my personal experience, the first example doesn't really happen:

It happens every day, all day. Other developers don't see it, because it doesn't even compile.

> I rarely see people randomly throwing annotations at methods/classes hoping one of them will stick.

I see it, plenty, in other projects. I have done it, trying to work around an arbitrary 3rd party restriction. I can't get something to work and see some old SO and hope it applies. Look at the arcane combination and through trial-and-error figure out what is relevant...then work backwards. Sometimes you can figure out what's going on without a blog explaining the opaque behavior or the overly-simplistic documentation explaining what something does...or used to do or doesn't always in some specific set of conditions, mainly mine. All you have to know is every bit of how each annotation works and why, and how that interacts with every possible element of your compilation and runtime. This is the unrealistic state of "understanding" annotations for the vast majority of java developers...or maybe it's just me and everyone I work with.

>When you use the wrong annotation, nothing happens during startup of your application... but you don't even know at which point in time something should happen. Or if.

Probably worth noting that this depends entirely on the annotation - they can (and many do) run at compile time, and can provide very strong safety guarantees.

Many (most? all? I dunno) of the bloated server-side DI frameworks do not do this though, and I 100% agreed that it's can be a truly awful experience.

At least we're seeing a movement towards compile time annotation processing. E.g., Micronaut, Quarkus, Dagger2.

Much better it fails at compile time.

> you get an enormous stacktrace from some code you have never seen and didn't even know was run, or why it was run, from a thread you have never seen, complaining about wrong parameters that you have never seen, passed from another method you have never seen

Aka using a library

That's more typical of a framework than a library. This is one of the areas where the difference becomes clear: with a library, you will see your own methods in the call stack somewhere, and be able to tell what got called and why; with a framework, it's often not so simple.
Regardless, this isn't an annotations phenomenon.