129 comments

[ 4.8 ms ] story [ 645 ms ] thread
(comment deleted)
Spring has gotten so bloated. It seems like there's no feature request they can say "no" to. Plus there's multiple ways of doing the same thing. e.g. JPA, spring-data.

their call stacks between injected components are sometimes a couple dozen layers deep and it makes no sense.

they don't provide easy upgrade paths between majors versions and they stop updating vulnerabilities on older major versions. I inherited some code that was using version x but had to be upgraded due to critical vulnerabilities. The vulns weren't fixed in x or y but only z. I tried moving over and their cryptic unit test fixtures stopped working. No docs on migration. Spring is sort of like cancer.

It would be pretty unusual for Spring not to take advantage of virtual threads, given the potential for dramatic performance improvements under many workloads.
> It seems like there's no feature request they can say "no" to.

Well the opposite of this is the antiquated world of "old Java". My subjective assessment is that Spring is somewhere in the middle: reasonable conservatism of principles but eager adoption of beneficial new things. Jumping on virtual threads is a reasonable action.

> their call stacks between injected components are sometimes a couple dozen layers deep and it makes no sense.

We're talking big enterprise-y software, right? Things get huge over time, no way out of complexity. The major red-flag would be accidental complexity which Spring, compared to the rest of the Java world, generally manages well, I'd say.

> they don't provide easy upgrade paths between majors versions and they stop updating vulnerabilities on older major versions. I inherited some code that was using version x but had to be upgraded due to critical vulnerabilities. The vulns weren't fixed in x or y but only z. I tried moving over and their cryptic unit test fixtures stopped working. No docs on migration. Spring is sort of like cancer.

Well non-trivial upgrade paths is the essence of a "major version upgrade". The alternative is to let old ideas never die and either stop introducing new concepts at all (old Java) or introduce them and make the framework bloated (something you criticized in the beginning of your comment). It's a trade-off which I think generally is well managed in Spring. It's boring, but in a good way.

> Spring has gotten so bloated.

I'd call Spring feature-rich than bloated. You can always shed weight that you don't want to carry.

> Plus there's multiple ways of doing the same thing. e.g. JPA, spring-data.

That's because there are different ways to solve a problem. Someone may want an ORM-based approach to connect to the database; they can choose spring-data-jpa. Someone may want to use JDBC with a light abstraction on top of it; they can choose spring-data-jdbc. It's all about choices and right tradeoffs and Spring offers plenty of them.

> they don't provide easy upgrade paths between majors versions

That's not my experience. I've been happily upgrading 2.x.x versions and plan to upgrade to 3.2.x when it is ready. But depending on the codebase, I admit it can be painful. Projects like OpenRewrite[1] might help here.

> and they stop updating vulnerabilities on older major versions.

This is not news. They want you to pay for extended support if you need it.

> No docs on migration.

They do maintain migration docs on GitHub wiki which are a lot more detailed than their blog posts on migration. Here's the latest one to upgrade from Spring Boot 2 to 3: https://github.com/spring-projects/spring-boot/wiki/Spring-B...

[1]: https://github.com/openrewrite/rewrite

My biggest issue with "feature rich" is that unless you have strict control over your developers' usage (which won't happen in most large companies), you'll inevitably have all sorts of bloat used by various teams in your organization, which just adds to the cognitive overhead for anyone who needs to work on those services. You know the old adage: once you provide something in the API, you'll have to support it forever.
> Spring is sort of like cancer.

That itself is kind of polite way of putting it. This thing is freaking pandemic virus. Spreading in all sort of projects including code with single CRUD operation.

All of these nice features of the JDK yet most Fortune 500 companies probably still using JDK8 or 9.
With extended support available for JDK 8 until this end of this decade or so, it's going to be a while before the real laggards upgrade, heh. Maybe that'll be my generation's method of funding the retirement RV purchase like the COBOL grognards circa y2k, haha.
Who's using JDK 9? I understand the JDK 8 bit - the breaking changes in large code bases are a big lift, especially in projects with no significant changes for years. However, if you're on 9, the lift is much lower and primarily on QE. Why stay on 9?
For large organizations it's also an organizational lift, plus their attraction to have all the latest goodies from the latest JVM versions is probably a little less, so a lot of the same inertia that existing in moving from 7 to 8 and from 8 to 9 is going to resurface for any migration, even if the actual breakages during the compilation step are less.
Most companies I talk to have a mixture of 8 for many stable unchanging applications and [11, 17, latest] for newer stuff.
That's because most of them haven't "really" migrated to containers. Once this is done the process of using a newer version of Java will become seamless.
Jar files are containers, in some fundamental way, I'd say.

Perhaps since they come from Sun and Solaris had zones, et cetera?

And not, in a more fundamental way. Sharing a JVM just isn't the same thing as a sandboxed process.

OSGI was probably the closest thing that tried to embrace that model, and it didn't turn out very well (putting it mildly). If the JVM actually did provide the same guarantees and restrictions as a real container, that would have been very useful.

In my experience it's more because their compliance team hasn't blessed the newer version for general use. These large organizations don't put the autonomy with individual teams to make such decisions to upgrade technology. So everyone is looking at a centralized authority who then feel the pressure to make a perfect decision, which turns into a hesitation because they also don't have a great process to evaluate a complex piece of technology like the JVM and declare it "perfectly safe and secure". So then the status quo wins. They will even pay companies like Azul to maintain support for Java 7 or 8, rather than making the leap. (And yes, there's probably a small contribution to this problem from source code that's hard to migrate, but as good as Java's been about backward compatibility, I don't think that's a big contributor in earnest.)
That’s pretty incredible hubris, to worry that something as central and widespread as Java could suddenly stop being safe and secure in a way that only your org can detect or care about.
It's more that their policy doesn't have enough detail to distinguish between Java and one dude's GitHub project. They'll have one policy for "software", or if you're lucky then two policies, one for "software we have a support contract for" and one for "software we don't have a support contract for".
In return the process of using a new version of their container orchestrators/runtimes/etc. will become more painful than a Java upgrade ever was.
Thankfully Java 11 LTS is now the bare minimum in our projects, and I even managed to push for Java 17 LTS on the latest one.

Even Android team has acknowledged that despite Kotlin, they needed to improve their support due to third party libraries, with Java 11 LTS last year, and Java 17 LTS coming this year. Both down to Android 12, naturally the usual subset without the graphics/UI related packages.

Some significant number of projects still use 8, but all indications are that they're not the majority anymore (I don't have information about Fortune 500 companies specifically).
Isn't it a success when you can build something that lasts 10+ years?

Enterprise has been relying on Java's compatibility promises for decades. It means the business decides when to invest, not the technology. The cost of re-validating and re-integrating a new system far, far outweighs that of re-writing the software, so you only upgrade the software when you're already having to upgrade the system.

Mark Reinhold managed to move Java to open-source, to frequent releases, and to supporting alternative languages (Kotlin for Google) and deployments (graalvm for containers) -- all while maintaining compatibility. He entirely effaced the notion that change means breakage.

Compare the breakage story in Python and C++ in the same time period.

To be fair, JDK8 represents a stable version of Java features and API that is most of what is needed for enterprise Java development. It is basically the stable inflection point of Java's evolution in terms of why Java took over enterprise development.

There was a core development intelligence level that JDK8 represented a sweet spot for (ahem, offshoring development). Which is really the core of the hate for Java these days although ... isn't Go the same trap?

Most of the post-JDK8 features are moving the language to what Groovy is, but closures, magic string templates, etc, but the IQ level of the programmers for that is a different market availability. Combined with the inherent deployment churn needed for modern JVM version turnover, which most enterprises simply don't have the organizational ability to do, means that JDK8 is going to stick around a LONG time.

Nonsense. For that kind of lowest-common-denominator development JDK8 has nothing that JDK6 doesn't; it has some minor quality-of-life improvements to the language, and some performance improvements to the VM, but nothing truly fundamental. If enterprises were trying to stick with a dumb Go-like language they would stay on JDK6.

The real reason is simply that JDK9 was a much bigger compatibility break (in practice if not in theory) than any release before or since, and came after a long time of relatively little development. A lot of frameworks weren't compatible with JDK9 until years after release; some older ones may never be. So especially for enterprises that may have their own large internal frameworks, it takes a while.

> For that kind of lowest-common-denominator development JDK8 has nothing that JDK6 doesn't

I see you are also one of the folks who ignores try-with-resources. And, say what you will about lambdas, it is night and day different between pre-8 and post-8 implementations of those various one-method interfaces such as InvocationHandler

    Connection fakeConn = (Connection)Proxy.newInstance(cl, new Class[]{Connection.class}, (proxy, method, args) -> { /*something awesome*/ })
I like both those things but they're just syntax sugar, they increase expressiveness at the cost of a more complex language. If the goal was a dumb language for dumb offshore developers (I don't think it is, but that's what the post I was responding to was saying), JDK6 is an even better choice than JDK8.

Putting it another way, the changes going 6 -> 8 are the same kind of things as the changes going 9 -> 10, or even 9 -> 17.

Somewhat off-topic, somewhat related:

Is Hibernate moving towards Java generics, about 2 decades after the fact?

Spring seems much bigger and seems to moving much more quickly, but it's weird that Hibernate seems to still be stuck somewhere around Java-in-the-00s.

Hibernate is probably held back by JPA, which evolves slower than Java itself.
There are things they can't do due to erasure but they use generics quite a bit in the API. Spring Boot uses generics for the CRUD interfaces etc.
It has supported generic List and Set interfaces for one-to-many and many-to-many mappings since Hibernate 3 I believe (~2005).

What did you have in mind?

I keep reading negativity about spring. I got curious, am building the backend for a new startup with spring boot 3. It’s been a breeze. Everything works, great tooling, very mature software. Am I missing something? Will I regret this in six months?
No. Lots of spring hate is from a decade+ ago. Spring boot plus jOOQ is a very productive backend.
Spring of a decade plus ago was fine, it just required a day or two of upstart to configure the whole deal if you weren’t already familiar and had a good project to reference as a basis for bootstrapping.

The tradeoff of today with Spring Boot is you might get bitten down the line with a little configuration mystery, although it’s such a well-blazed path you’re probably in good company and will find your error solved and documented.

In addition, it’s Java so virtually any serious task is done in a well-blazed fashion with numerous open-source options available. In contrast my Node library I used to generate and validate an XML document at work last month cannot read in its own XML output. It’s a boon for efficiency.

The only downside Java really has is that it’s a bit harder to “hack on” a dynamic, evolving JSON structure so it may be a bad fit for organizational reasons if your company lacks a cohesive, mature Engineering staff.

my biggest gripe with old Spring was how far you had to go to get "hello world" on the browser when starting from zero. I never would have thought embedding the whole application server would be a good idea but SpringBoot proved me wrong. That plus the annotation based config vs xml files is what made fast development with Spring possible (in my mind).

Also, Spring would be a miserable experience if it were not for Maven/Gradle. Have you ever counted the jar files included in a typical SpringBoot+Spring-Security+Spring-Data app?

> my biggest gripe with old Spring was how far you had to go to get "hello world" on the browser when starting from zero. I never would have thought embedding the whole application server would be a good idea but SpringBoot proved me wrong. That plus the annotation based config vs xml files is what made fast development with Spring possible (in my mind).

All of this is long before Spring Boot. Embedding the web server so that your system is just a plain Java application is a great idea; annotation based config is a great idea; both of those are things you could do (and I did do) back in 2010 on Spring 3.3.

The stuff that Spring Boot adds - the spooky-action-at-a-distance classpath scanning, the we-can-use-them-but-you-can't annotations, the way you can't override part of an autoconfig but have to either accept what the library gives you or write a full config from scratch - is a big mistake. Those who forget Java EE are doomed to repeat it I guess.

> Also, Spring would be a miserable experience if it were not for Maven/Gradle.

Sure. "Have dependency management that doesn't suck" is good advice for any software ecosystem.

I think older spring was fine, but many people complain about xml configuration as a big one. And, like you said, it took time to setup. Spring Boot today with modern Java is a really nice backend environment to work in.
You won't regret it. Huge community, great docs, good enough performance, great testability. Integrations for everything.

It's boring tech, which is a good thing IMHO.

I think that’s the source of negativity for the HN crowd. It’s boring, predictable, mature enterprise grade software that quietly gets the job done. It’s too similar to what they are forced to work with in $DAYJOB, instead of the exotic, fun frameworks they’d rather be playing with that you always read about here.
Well, not exactly. HTMX, for example, is plain, simple, boring - everything but exciting. But people here love it. Why? Because they don't have to deal with overcomplicated crap for no reason.
It's new
Not that much - it's previous incarnation (intercooler) is almost 10 years old.
Spring Boot is very productive but things can get very hairy when debugging auto configuration or understanding why a bean is missing from the context.

Running Boot in a container is also not great due to the memory requirements needed for bootstrap (min 0.5 Gi). I know GraalVm makes this better but compile times are still too painful.

I also think the push to make Reactor the default choice was a mistake (deprecating RestTemplate). The reactive model in Java feels unnatural and it can be very hard to debug.

I am working with Go now and don’t miss Spring Boot that much but it still has a place in my heart for the amazing community and docs.

Spring and the JVM (even modularized) is a huge sore for cloud ops due to container size. Great that you pointed that out. Go's chi router or Fiber along with gorm gets us not so far away if you're doing crud. I also love go's ability to bundle assets into the binary and serve them (front-end code). It's so good that if I want to run the entire web application on another machine, I just copy the binary and run. Docker memory requirements are in the 64m range and even that is probably a bit of overkill. I can run thousands of these on a raspberry pi 4. I can only run like 4 spring boot apps on it.
Which stack/frameworks in Go would you recommend to achieve a somewhat close developing experience? AFAIK the default library doesn't have much and there are no huge frameworks like Spring.
I have been building with Spring for over a decade. You won't regret it. It's just stable, and works and something you use to build things that last.
Usually HN culture with their Python and Ruby stuff.

I wouldn't change Jakarta EE, Spring, ASP.NET for framework of the day, with a subset of functionality and molass performance, unless told otherwise by customer architect.

You will not, it's a fine choice for a software stack. There have been startup time problems, which Spring has been doing an excellent job of addressing. There's also been some silly performance problems which afaik have been addressed.

We personally use Quarkus and it's also pretty good. I think the Quarkus story has driven spring towards having a better story and both are pretty excellent stacks to go with.

Spring is old. The negativity comes from the the time where ppl would jump to Rails or Django instead of dealing with the config explosion/unnecessary complexities for startups/verbosity of the java at the time.

Now, we have modern java + simpflified saner default named spring boot, that's a different story

Spring boot has driven spring to be a lot simpler as well. Further other frameworks like Micronaut and Quarkus have driven spring boot to be better.

There's been some real symbiosis among the backend frameworks that has caused them all to lift in quality.

Kotlin + Spring/Boot is an amazing combination for backend work. The Spring/Boot Team have poured a lot of energy into making it very graceful to use.
> Will I regret this in six months?

Only if you plan to collect kudos with it on HN. Otherwise you'll be fine.

Spring is too automagical, often breaks with new releases, eats incredible amounts of memory, requires too much time to deal with its problems, produces stacktraces longer than my program. It's terrible, honestly.
To add to the sibling comments, you'll also enjoy the monster amount of introspection made available in the combination of the JVM and JMX exposed via the Spring actuator endpoints. We regularly adjust logging levels of running pods in order to sniff out errant behavior versus bouncing them to fiddle with some log level. I have yet to play with a software stack that is designed for as much in-flight tuning as Spring on the JVM

Don't overlook @ConfigurationProperties <https://docs.spring.io/spring-boot/docs/3.1.1/reference/html...> as a way of strongly typing any configuration knobs that your application may require. That not only allows static typing to avoid the hazardous @Value("some.vaule.oops") but it also means that IJ will tab complete (with typechecks) in your application.{properties,json,yaml,whatever}

Despite the all-in opinion in this particular thread, my take is a little more nuanced. The modern versions of Spring are a breath of fresh air (no pun) compared to the older ones. It still does have a bit of a kitchen sink feel when you build something non-trivial, and some of the magic of the boot approach can become hard to reason about. You may prefer a simpler worldview of an alternative runtime, and you can also benefit from shorter compiles/quicker startup, etc.

That said, the downsides aren't really that bad. It's got a strong community, will obviously still be around in 5 years, and the JVM is seeing strong investment and modernization. All in all, it'd be a strong contender if I was putting my money on the line. I'm still a fan of the simplest-thing-that-can-work approach (in the old days it was Jetty + Guice + whatever else you wanted), but modern Spring isn't all that far off from the experience anymore.

one nit pick, Spring is more than Spring Boot although when people say "Spring" they're likely talking about Spring Boot.

You won't regret using it in 6 months except maybe for the fact that your backend will be just working and doing its thing and there's not really much else to do except watch it work.

Spring before spring-boot was an unreadable sea of unique xml. Spring-boot is a guardrail that steers you into the correct usage of the spring framework.

spring => configuration over conventions

spring-boot => conventions over configuration

Mostly agree with this, the issue was that Spring Framework 3 introduced a new way of doing configuration with the @Configuration annotation. Up until then it was all xml driven (some minor packages like spring-integration still required it) bean soup. The advent of the annotation driven configuration paved way for spring-boot to provide sane defaults to the framework (previously reserved for certified spring framework engineers like myself to define in xml that we copied time and time again).

You nailed it though with Spring being configuration over convention and Spring Boot being the opposite. At the time, there was a competing web framework that was really simple to build with called Rails that made the mindset shift to ease-of-use from a dev standpoint away from ease-of-use from an ops standpoint.

I disagree but know I’m in the minority.

I would take Spring config XML over the tightly coupled mess of Spring config annotations and code. Circa 2006, Spring config was about binding decoupled POJOs together and code didn’t require any build time dependency on Spring. Now Spring is required at build time and objects expect to be instantiated as beans, and unit tests either require a minimal spring context or some other method of injecting dependencies that would have otherwise existed as a constructor arg or mutator anyway. Now “config” is fully Turing complete as opposed to declarative. Yuck! I’m no fan of XML, but config as code is no longer config, imho.

I liked original Spring idea of being actually non-invasive framework as well and I never had any issues with Spring XML config. It worked for me and Idea has decent autocomplete for it.

There was time when Spring was considered lightweight. Oh, well.

Spring Boot is when it all went downhill.

I'm sure you can find terrible implementations using Spring/Spring Boot. But the only annotation you have to use all the time is @Component (or @Service) and you could still externalize that, even without XML configuration, by using @Bean in an @Configuration class. Even if you're not so worried about compartmentalizing your bean wiring, the number of annotations is often very minimal, and you can focus a lot on actually writing your best business code.

At the end of the day, complex applications need to deal with complexity somehow. The debate becomes: do you want to adopt and trust Spring's "under the hood" complexity, or is your team going to roll a lot of that themselves.

Typical spring boot application is absolutely full of annotations and extended helper classes for configuration, and implicitly running starter classes for further configuration.

My Spring Boot application launches entire tomcat under the hood without a single line of code. How terrible is that?

My preference when it comes to configuration is just pure code. Not @Configuration. Just construct necessary objects, wire them up and go. Just like with Golang. So no magic, all I wrote will be executed and no more. No classpath scanning will be performed. No code will be generated (I can tolerate code generation at pre-compile phase, so I can easily browse it with my IDE, something like mapstruct, but I hate all those java.lang.Proxy-ies, all those asm-generated wrappers around interfaces just so I can write @Transactional, like it matters).

I can actually fight Spring Boot. I can write XML configuration or configure things with code myself. But I live and work in community and I expected to follow so-called "commonly accepted" practice.

> How terrible is that?

Not terrible at all? For most people (not you, OF COURSE) I would ask: could you run Tomcat better than Spring can? Then why care?

The point isn't that one should reinvent the way that Tomcat is started, but that Spring Boot (by default) is using action at a distance and runtime reflection which have serious downsides if you want to understand what's actually going on because you're a) new to the technology, or b) have to debug some weird edge case.

The alternative is using explicit, reflection-less code - which you can do even with Spring, although it's experimental: https://github.com/spring-projects-experimental/spring-fu

Spring annotations have runtime scope (one annotation per class is usually sufficient for injection if Spring can resolve all constructor arguments). You can disregard them and wire your POJOs manually via constructors in unit tests. Spring context thus is really required only in integration tests, in unit tests it is optional and may be a code smell (testing too big scope).
Spring Boot takes care of bundling libraries comprising the Spring universe into version compatible units. That is pretty much it.
No, you won't. People who gripe about spring didn't bother to do things the spring way. If you wrote your own DAL, you did it wrong. JpaRepositories and the like are the way. If you wrote your own JSON serializer/deserializer you did it wrong, Spring includes Jackson and will do it for you (YMMV). If you rolled your own authentication, you did it wrong. Spring Security can magically lock down your app with as little as a single configuration method, providing jwt/oauth/openid-connect enterprise security out of the box.

The issue with spring boot is not it's feature-set or it's robustness. It's with it's heavy handedness to force you down the "spring way". Annotation driven. Extending provided interfaces. Making it "sticky" and impossible to remove. Want to introduce gRPC? yeah, you're going to have to find a way to weave that into Netty or use a different port. It's robust enough just up unto a point where you hit a roadblock and have to dig deep deep into Spring land to understand whats going on.

It's still the best framework for standard crud web api's and such with Java. Though micronauts is quickly approaching. You can't go wrong choosing spring boot in 2023 if your intention is to ship in 2024. Whether or not that codebase is solid in 2027 is another matter.

Sorta random, but I really appreciate this kind of post on HN. People have tons of domain expertise and hard-earned lessons that get shared like this, and it broadens my awareness without requiring any time investment on my part.

Thank you, thank you (and others like you) for taking the time to type this up!

I’m shocked actually that you don’t get this kind of mentorship where you work. What’s your Director doing? Not that we’ll be building spring apps but knowing where we, as an industry, have came from and tried things, we’ll avoid them in the future. My heart sinks when a good idea on paper, but bad IRL, get reimplemented 10 years later because someone didn’t bother to read history. That said, sometimes time is all you need for tech to catch up. Rarely that’s the case though.

So yeah, you’re welcome, thanks for coming to my grey beard tech talk. Ask your managers for more brown bag/lunch & learns from the older folks at your company - they have stories.

> I’m shocked actually that you don’t get this kind of mentorship where you work. What’s your Director doing?

Bold of you to assume that every company has a technically competent director (or that higher-ups even care about mentorship).

I’m sorry. Really. You should be valued enough to warrant skilling up. Future road maps depend on it.
Annotations are great until something breaks.

Fundamentally they are an abstraction over a rats nest of code (of a specific version), library dependencies and configuration. Gods help you when something breaks, the abstraction leaks and you are presented with a glimpse of the underlying complexity that is now yours to tinker with.

Oh don’t get me started when competing data source beans are defined. Which one will @Repository pick up? Which one will it use at runtime? Why is my stack trace 245 deep?
I can't speak to Spring, and I've never worked with it directly.

I'm from the Java/Jakarta EE parallel universe. Where everything is the same, but different. Both universes have impacted each other is various ways.

JEE has a lot of baggage and notoriety, but, to be fair, so does Spring as they both came up in the same domain at the same time. The XML Hell that JEE was notorious for afflicted Spring as well back in the day. Spring has been more nimble and brought many things to light before they hit the JEE standard, but JEE marches to a different drummer.

Modern JEE is pretty darn good. JAX-RS for web services is really nice, and there's a lot of different deployment options. A lot of the things you can love in Spring you can find in JEE, a lot of the things you can hate in JEE you can find in Spring.

Again, not able to speak to Spring, but JEE has been really stable over time. In that old code continues to work even in new containers. There's a good chance that could even deploy an old, creaky "J2EE" application from 20 years ago in a modern container.

The most disruptive thing to happen to JEE was the Jakartatization of it when it moved from Java EE to Jakarata EE, and all of the namespace changes that happened between Java EE 8 and Jakarta EE 9 with the move from Oracle to Eclipse. But even then, you can still deploy old code in new containers. This was far less disruptive than it could have been, frankly.

I'm certainly not going to suggest to not use Spring, but at the same time, I wouldn't discard JEE out of hand either. It's also an exciting space (for assorted values of "exciting" and "enterprise") for back end work.

> There's a good chance that could even deploy an old, creaky "J2EE" application from 20 years ago in a modern container.

Just no. No. All the different types of EJBs… won’t work. Neither will SOAP endpoints. And a shit ton of other stuff that JEE dropped. What are you talking about? Even XML parsing moved packages.

He must still be stuck on JBoss at work…
Spring was the reason J2EE is better today. It is the reason for existence of Interface21 if you can remember. I think your bad thoughts are tied to the J2EE monster and dumpster fire that goes with it.
Sometimes the spring way is also just ugly, or confusing if you just want to do thing X. This code here is used for making HTTP requests using OAuth2. There are probably other ways to do it but I've seen this code in more than one place. I speculate that most people didn't knew they had to use a "AuthorizedClientServiceOAuth2AuthorizedClientManager", but rather found an implementation online and copied it.

But as you pointed out, since there are established spring ways to do something, it doen't matter if it's ugly since it actually "just works".

```

@Bean

public AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientServiceAndManager (

        ClientRegistrationRepository
        clientRegistrationRepository,
        OAuth2AuthorizedClientService authorizedClientService) {

    OAuth2AuthorizedClientProvider authorizedClientProvider =
            OAuth2AuthorizedClientProviderBuilder.builder()
                    .clientCredentials()
                    .build();

    AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
            new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                    clientRegistrationRepository, authorizedClientService);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

    return authorizedClientManager;
}

```

Now make it with user-configurable credentials (like extracted from database) with proper token caching.

With similar code I almost made a vulnerability in my app because Spring cached tokens which belong to different users.

Spring security is terrible piece of code. Spring - I can stand it. Spring Security - I can not. It was different project back in the day (Acegi Security if I remember correctly). It should have stayed that way.

This already uses user-configurable credentials. It uses an autowired "ClientRegistrationRepository" that provides the credentials.

We load the credentials once from AWS SecretsManager but I'm sure you could also load them from some DB.

But yes, I'd probably prefern a modern rewrite of Spring Security.

And yet, it is probably still much more secure than what 90% of us can whip together. I mean, maybe there's something better out there. I'm just pushing back on any suggestions that you should rather roll your own. Because, unless you're the other 10%, in an organization where continuity of that skill-set is taken care of, you definitely should not.
Tangential but I really wish HN supported monospaced code blocks using 3 backticks. Reading code blocks on a phone is terrible here on HN
You can do it with 3 spaces in front. Not the same as backticks but works just as good…

   public class See {
      public See() {
      }
   }
You just have to use a scratch
I usually begin to rant about the verbosity and unintuitive ways of overriding things in Spring when I have to do it, but then stop and realise I'm grateful that it's actually possible and I don't have to throw out the framework or do some extremely ugly hacks.
Spring DAL is really debatable. My view of it is that it is one of those 'fast early development' at the expense of maintenance. It's easy to write code, modifying it later is difficult. Thus I feel this is an optimization to make writing easy code even easier. Therefore the pain of the Spring DAL is unexpectedly slow queries that are doing N+1 selects, having to translate SQL to JPA queries and back [+], dealing with the ORM issue that Spring models database rows and not database results, selects that retrieve far more data than they need, and the unnecessary situation of having to build a full object model to represent database (which is often unnecessary! If my only query aggregates 4 tables and returns two string values, there should be no need to model those 4 tables as full blown Java objects - I'm clearly a fan of things like MyBatis or JDBI for replacing the Spring DAL access. I've been embarking on that very effort at my job, so far replacing Spring JPA to JDBI has reduced a ton of code, was able to recently remove net 40 classes by replacing JPA with JDBI).

For example, some queries are naturally best done as analytic queries (or window queries). I have no idea how to do that in JQL (JPA's query language). Moving those queries to the database side can come to the rescure, but it's still an issue.. Which makes me think that JPA makes the easy queries even easier, but the difficult queries nearly impossible (if not outright impossible).

> Though micronauts is quickly approaching.

I've been looking at micronaut quite a bit as well. Another big downside to springboot is that by default it wants around 100MB of RAM per instance. With 6~7 applications on one host, the expenditure for the memory is sizable. Micronaut has a significant leg up here.

[+] DB Queries are often developed first by figuring out what the right query is. After that is done, it needs to be translated to JPA. If JPA eventually starts doing the wrong thing, or something slow - it then needs to be converted to SQL so you can do an explain plan. The latter is not that hard as you can enable a debug output to see what it is, but overall there are quite a few more steps here compared to: "develop your SQL query, next plop that into a string variable in your java code", vs "develop your SQL query, model the table relationships as java objects, build a JPA repository to run some queries against those tables" (in the happy case the latter is all that you need and is not bad, but when a query starts going slow, or starts fetching MB of data which you won't notice on local development but in production causes a p0...)

Spring DAL is geared towards operational data stores where typically you are not joining four different tables. Its been a while but pretty sure you can just write the native SQL and map the results to a collection, bypassing mapping to objects. The best strategy I've found is to make the domain objects one-to-one with the tables so their is no joining or inheritance. Do any combining within the repository or application layers. This makes the DAL super simple.
If you really really really have to, you can use @Query to define your own query for the repository interface method. This allows you to map, join, select, whatever you wish. The gripe is that if you don’t follow a specific wording convention, you must use @Query. JPA follows a specific convention - findByUsername or findBy (id) or findByPostAuthor(String name). There’s a whole section on how it derives sql statements from these methods.
The magic naming in JPA CrudRepository can be pretty nice.

Though, even resorting to @Query is not a panacea. Notably it breaks down for things like dynamic where clauses, dynamic joins - and you wind up using entity manager directly.

This really good article [1] on how to use native queries has some good examples where the native query is very clean. Though, the article ends with this:

```

Query q = em.createNamedQuery("selectAuthorEntities");

SynchronizeableQuery hq = q.unwrap(SynchronizeableQuery.class);

hq.addSynchronizedEntityClass(Author.class);

List<Author> authors = (List<Author>) q.getResultList();

for (Author a : authors) {

    System.out.println("Author "

            + a.getFirstName()

            + " "

            + a.getLastName());
}

```

For a simple "select first, last from author", the above seems kinda insane. Further there are issues with the requirement that Author now be mutable.

It gets even worse when it comes to the primary key and how that gets mapped. At work we have an ancient database where you cannot invoke a sequence within an insert statement. The ID generator for JPA throws a fit around this; it really wants to know the sequence and to use it. Hacking around JPA to not throw an error when we set the ID value directly is painful. JPA assumes that if an ID value is set, then your invocation of "entityManager.persist(entity)" will be an update. For us we have to set the ID value, and it very well is not an update operation that we wish to perform!

Thus a simple task of inserting a new row and fetching it goes through different techniques, is required to have an object mapping, and breaks down suddenly if used in a different environment where the DB version changes (and of course, it is only production that uses this ancient database). For the use-case of the original developers this was fine, when looking at it now, with 4x the data and seeing that we do a N+1 query - the whole thing is a mess to work with as we have some join data that sometimes needs to be lazy fetched, and other times it does need to be eagerly fetched. Thus having duplicate DTOs to represent this situation because we need different annotations is a headache. So I've been facing with existing simple JPA entities that conditionally need to lazy fetch vs eager, and that's really painful.

These are all tractable problems, but overall it would have been better/simpler for it all to have been pretty bare-bones SQL without any of the ORM layer. My snarky consolation is that at least those original developers got to save an hour or two when jamming this original product together in the first place. This is why I think SpringBoot + JPA are particularly good for toy demo projects, but something that is going to live for a decade and need serious changes and deal with serious changes in data scales - it's those difficult things that come later that are now requiring tons of time to implement. Some of those difficult things would be trivial to do had it all stayed as just SQL with mapping of results to objects.

[1] https://thorben-janssen.com/jpa-native-queries/

--------------

Caveat, I am just saying it is debatable whether to use SpringBoot's DAL, or whether to avoid it and use something like JDBI instead (I strongly prefer the latter, and of course at work, that is what we're doing. The stuff that has been converted to JDBI performs really well because it fixes the lazy vs eager join problems, eliminates N+1 queries and always selects just exactly the right amount of data, and we can use advanced query structures that lets the database do the work (eg: case statements, analytic queries, the aggregation functions of DB, etc) - and it's been easy to test and has eliminated tons of needless JPA class mapping code that was infle...

Jpa ID’s can be any serializable with a matching column type. If you’re going down to the JDBI level or doing jooq to get at your data, there’s a problem. I’ve found most of my issues with Jpa and Hibernate specifically are due to relationships and my table design. Sometimes just having the foreignkey and not the full relationship is the answer, sometimes writing a custom @Query is, but never has going down to the jdbc level been pleasurable. Even looking at your example above, it’s something I would flag in a code review as being not novel and not trivial and therefore complicates the code base unnecessarily. SynchronizedQuery can be simplified with the synchronized keyword in modern Java on the interface method of your repository.

For a really large spring project with hundreds of tables, we abandoned entity relationships outside of FK’s and instead did our joins in @Query’s, named queries (views essentially), services (to join repositories) and some components to share cached results in-memory, across nodes. What kind of ID were you trying to use and did you create a generator for it? IdentifierGenerator interface

> Even looking at your example above, it’s something I would flag in a code review as being not novel and not trivial and therefore complicates the code base unnecessarily. SynchronizedQuery can be simplified with the synchronized keyword in modern Java on the interface method of your repository.

Apparently that lengthy bit of code is to specify the query space. That call to "hq.addSynchronizedEntityClass(Author.class);" is seemingly a critical part.

I learned a bit though to consider whether that could be simply sychronized (I appreciate your response!)

From that same blog post:

>> The query space describes which entity classes your query references. Hibernate uses it to optimize the dirty check and flush operation that it has to perform before executing the query.. You can do that by unwrapping Hibernate’s SynchronizeableQuery from JPA’s Query interface and calling the addSynchronizedEntityClass method with a reference to your entity class.

I agree that is not trivial and complicates the code base unnecessarily.

For comparison (just putting this out there), the equivalent JDBI (with no concerns over dirty checks, flush operations, or query spaces) in nearly its entirety is :

```

List<Author> author = jdbi

     .withResultMapper(BeanFactoryMapper.of(Author.class))

     .withHandle(handle -> handle.createQuery("select first, last from author")

     .mapTo(Author.class)

     .list());
@Data

class Author {

  String first;

  String last;
}

```

I particularly like how this moves so much knowledge into the query string and away from the java classes. The above could be a far more complex query with joins, aggregation functions, etc.. and no new java classes would be needed, just a longer query string. In JPA, there is no query string of course. In JPA, the complexity of a more complicated query is pushed to additional classes with data fields and annotations.

This is just a different characteristic of how one scales with complexity vs the other. Whether ORM vs result-set-mapper is better depends on context.

It's all about picking the right tool for the right job. There are difference in how they scale relative to needed queries, complexity of data model, it's very situational.

As an analogy, I think ORM is like PHP - great for getting started and building some pretty big stuff very quickly. For a larger system I would not recommend PHP. I find JPA to be very hard to maintain once scaled to larger code bases with non-trivial relational data.

> What kind of ID were you trying to use and did you create a generator for it? IdentifierGenerator interface

It was a vanilla Long. The IdentifierGenerator suggestion is a good one, I think that might have solved potentially all of the problems we had with the PK values (there were others). I think the crux of that issue was the existing annotation worked, but then we also needed it to work for a very ancient version of Oracle where it no longer. The required annotations for the two different versions of Oracle were not compatible with one another.. just messy.

The issue of 'sometimes eager fetch' vs 'sometimes lazy fetch' was a large problem at the same time. I think that was perhaps a difficult problem because an '@Eager' was added to a number of fields already so teasing these different scenarios apart for all of the existing queries involving a number of entities was going to be a very non-trivial process.

-------------

I'm just pointing out there are trade-offs when going with an ORM vs a result-set mapper. Both are better than doing some direct JDBC work. Spring+JPA pushes you into an ORM philosophy, which still for sure has its place.

I agree with you. ORM is for business domain object that you know of, are certain of, have clear definition of, and are the core data structures for your app. If you have to explore data in unintended ways, result-set mapper is useful. Another option is to provide an analysis tool that weaves into your app allowing you to run result-set mapper queries against your data source without having to make it part of your data access strategy in your application. Just a thought. Spring+JPA is designed for business, boring relational business, 99% of the time.
>Another big downside to springboot is that by default it wants around 100MB of RAM per instance While it might be not applicable to every app, nowadays we can do native images with Graal and some cheesy app could be launched with under 128MB of RAM usage.
What about something like ktor[0]? This looks promising to me, at least.

[0]: https://ktor.io/docs/welcome.html

ktor is best fit for Kotlin applications. But, much like Play Framework - you can use it with just about any (or any?) JVM language.

With that said, Spring Boot will be vastly more mature, have a lot more resources available, bigger community, etc. There's also tons of 3rd party libraries that have support for spring and/or spring boot out of the box - such as resiliance4j, openapi-generators, etc.

With that said, Spring/Boot's Kotlin support is really good. The team has clearly put a lot of effort into making Kotlin a first-class language within the Spring ecosystem.

For backend work, Kotlin + Spring Boot is amazing.

I would agree with most except Spring Security is not so simple due to its power and a number of changes over the releases. Trivial cases are simple to do but anything beyond that requires understanding the beast and that takes real effort. More complex cases are then again relatively easy.
We experts have to pay our bills somehow… I joke but you’re right. Spring Security works up until a point and when you want to make it do exactly what you want it to, you have to crack it open-override or extend-implement your own class (cough UserDetailService cough). Still, if you’re doing microservices with spring, spring security is enough to ensure your jwt in the request is valid. I like to pair it with more robust auth like Keycloak though if I’m going “platform” instead of monolith.
> People who gripe about spring didn't bother to do things the spring way.

Of course, everyone who complains about a technology just hasn't learned it properly.

I think I understand the Spring model fairly well at this point. But one of the problems with it is that it's confusing and that the runtime-reflection-heavy approach causes issues with debuggability, performance and also errors creeping into production that should by all accounts be preventable at compile-time.

It's rather easy to make silly mistakes with Spring, such as confusing @Configuration and @ConfigurationProperties or writing tests that share the application context even though they require different configuration and suddenly stop working if they're executed in the wrong order, and even if you yourself learn it perfectly, your coworkers might not.

I agree that it offers a ton of benefits - but that doesn't mean that it isn't valid to criticise it.

> Am I missing something?

There's a huge difference between "Spring pre-Boot" and "Spring post-Boot."

Not quite as significant as Angular 1 vs Angular 2, but close.

In six months no but in 1-2 years yes. Spring has large tentacles so that when you start using it all code is coupled to Spring Boot and it's dependencies. I.e, the code you write is useless without Spring Boot, it is difficult to separate your code from Spring Boot. Now when technology changes you will have major refactor work i.e, like when Spring Boot moved from Dropwizard to Micrometer. It is possible to mitigate but takes advanced skills.
If you drink the kool-aid, you'll love it.

It's a tricky question to get a real answer, due to all the vested interests, lack of real research, and differing views of "what is good"

Which is better, Amiga or ST (Amiga!)

Spring is very good software, and basically and object oriented language will need to use stuff like it after enterprises throw their stack of requirements at it for 20+ years and following 20 years of technology evolution.

Yes its been tied to many java architectural fashion boondoggles of the past (XML config madness, XML namespace magic, overly heavyweight serverside HTML rendering, JSP tag madness, software patterns) but I still turn to it for basic code structuring in any JVM app.

Boot is pretty good ... until its not. If you are doing garden variety web services it gets you up and running fairly well. If you need to do anything fancy with routing or raw data in the html request/response, then it is not. And Spring is now so synonymous with Spring Boot that it kind of isn't a good thing.

Spring also isn't a pure open source project, it has a company trying to constantly steer it to revenue production, but as those types of entities go, stewardship has been decent. Normally things like that have about four or five years of trying to use open source for market expansion before impatience/greed/frustration sits in and they go for the closed source grab (see: Hashicorp recently). 20 years is a remarkable run for something like that.

A stacktrace in a Spring app can be painful to see. Tons of Spring code inbetween layers of your own app. Sometimes it has made me wonder, what happened with just plain old method invocations?

Granted, it can probable be helpful in many ways, but it comes at a price. But I think improvements in developer tooling will make annotation-driven programming unmodern. I see a renaissance coming for plain old programming.

"Java is a DSL for taking large XML files and converting them to stack traces." -Andrew Back

https://twitter.com/9600/status/90729345307521024

Yeah, I think that is actually pretty insightful. But although that is what it has been it may not neccessarily be what comes next. I imagine developer copilots to take care of the mundande coding you would need to do without frameworks like Spring and basically make them unneccessary to a large extent. Instead you will have leaner apps, but with a larger proportion custom code, and less use of dependencies.
Spring does generate some giant stacktraces, but if you have all of your code in a unique package structure (like com.mongol) just jump through your own code by searching that name.
You probably won't regret it, Spring is a solid framework. But the issue with Spring in my experience is that it has constant breaking upgrades with various dependencies But this is more of an enterprise issue. Upgrade breaks a dependency, fixing that dependency will break another dependency, etc. While some people say Spring Boot is convention over configuration, I don't really buy that, the configuration is done via annotations which is still configuration. My other gripe with Spring is that despite it all being annotations, when it breaks it can be opaque about why it broke, or why it didn't bind something.
> Upgrade breaks a dependency, fixing that dependency will break another dependency, etc.

If you just upgrade all your spring-related dependencies to latest-of-same-major-version every time you upgrade, on average how much does your own code end up breaking?

In that light pretty rarely. Which I suppose is a point in favor of Spring. When spring's dependencies break you usually can't even run the server.
I thought the same thing 20 years ago. Still using spring. It's not a panacea, but compared to other technologies which came and went, it's been a great investment. The same could be said for Java, linux, python, bash, vi.
For a new full stack project I'd use a full stack JavaScript framework, but spring boot is still awesome. Especially so when slow starts and higher resource consumption aren't as important as developer productivity, testability, reliability (which is pretty often).
But which one? Next.js, sveltekit, etc. are tied to frontend frameworks. Nest.js seems good, but basically Spring Boot in JS. A lot of JS devs seem to want more FRP based patterns (unless they like Angular), and the rest are just small frameworks that'll be difficult to hire for. Have I missed any? I want something fullstack and so more structured than just Express.
It's like you say, Spring Boot can get you from nothing to a production ready application in no time, it is reliable and the community support and documentation is great. It is also pretty flexible, up to a certain limit.

If your product happens to be something serving a zillion requests per second, using some exotic protocol, or doing some wildly complex calculations, then Spring might not be be the best choice. And it just so happens that the HN community's primary interest goes out to these rare exceptions and not to the 99% other projects in the real world.

Let me throw-in my 2c worth. I started my career in consulting with an in-house but Spring-inspired framework. I hated it, turned to other jobs that used other tech stacks; nowadays I largely consider myself a Pythonista. Lately, after about a decade from that first gig, I've finally decided to learn Spring properly and I've made good progress with it. Mind you, I decided to do so only because it seems like a good career move to know Spring in this economy.

So, my honest evaluation: learning Spring is frustrating and not an enjoyable experience at all. Just the sheer verbosity of things hurts my Pythonista soul. It's not just that Java is inherently more verbose than Python, it's just that "the Spring way" forces you to create three classes with little to no shortcuts around it; even the most opinionated Python/Node frameworks I've encountered wouldn't be that draconian. Coming from Python/Node/PHP/serverless, the framework's philosophy seems incomprehensibly monolithic.

All the layers of abstraction (and their resulting stack traces) make for an extremely confusing debugging experience especially for noobs.

*BUT*, I admit, judging a technology for its learning curve is not fair. I just had to vent my honest opinion on the experience.

I'm now at that point where I think I've grokked "the Spring way". That is, my Google searches on how to do X in Spring/troubleshoot a problem are smarter now. I've actually grown to appreciate it. (I'm pretty sure the Spring Gods would make me pay for the sheer hubris of this statement and will make me eat my words in the next feature I come up with in my side project.)

JpaRepositories are powerful magic. If Spring has any advantage over the frameworks I've come to love, it's this. JpaRepositories compensate for what Spring/Java lacks in brevity. Oh, also, not a Spring-specific advice but Lambdas and method references have made Java easier to write and has definitely deflated the number of classes I've had to create otherwise. Some would say that's just lipstick on a pig but I prefer to see it as an oasis in a desert.

Lastly, don't take this as advice for the backend you are building for a new start-up but I really see Spring's strength in that consulting CRUD-software market. If I can define specs in a legally-binding contract before writing a single line of code, if I can bang out a schema at the start of development and not worry about migrations, I would actually choose Spring. All the "corporate-rep" of Java won't hurt for these types of projects either.

But on the other hand, for environments where I need a lot of flexibility and lack the leisure of time almost always, I feel like Spring would be a development death march.

Misc notes:

1. I'm not actually learning vanilla Spring. I'm using Spring Boot alongside Vaadin. Overtime I've come to distinguish better between the two but maybe some percent of my frustrations in the learning phase is on Vaadin, not really Spring.

2. I made extensive use of ChatGPT in learning Spring. Even for such a boring/common tech, it's hilarious what ChatGPT gets wrong. But it also gets things right, giving advice in a level of clarity that blog posts and SO posts couldn't have provided. I would recommend anyone trying to learn Spring (or maybe any other framework/technology) to give ChatGPT a shot too. Even when it gets things wrong, the feedback loop that resembles actually talking to someone helps with the rubber ducking.

Again, all 2c worth of opinions from someone not even a year in this framework. Caveat lector!

Don't worry, all negativity comes from those miserable Go devs.
The negativism towards has two main points:

First, that Spring it's bloated. Spring now has a wide extension of modules that you may or may not need. Most of those modules won't even be added in your classpath unless specified in your pom.xml or build.gradle file. Very simple projects won't need the vast majority of the full Spring power.

Second, that a Spring project takes too much time to launch the web app (Compared against other frameworks doing the same task). Part of the reason it takes too much time to start comes from how the engine inflates the beans and injects it. On that same topic, other frameworks such as Micronaut are more lightweight because they don't bloat the framework as much as spring, thus the launch time is shorter. Also take into account that Java developers adore complexity in code structure so that also affects launch time.

Are you going to regret going with Spring? No, there is a lot of support from the community, if you have any issue you will find help in reddit and stackoverflow. It's a really mature framework.

However it's not the only option. There are other frameworks that could be a better choice for your use cases.

> Am I missing something? Will I regret this in six months?

Yes. One day you'll add another dependency to your classpath and your application will magically be doing something you didn't want or need. And the way to turn it off will be an undocumented annotation that only exists in the next version of spring boot, so you'll upgrade to that, and then a whole bunch of other stuff will break.

Java 17, Hibernate, JPA, Spring Boot - all the way! Excellent combo! Reliable, high developer productivity, excellent community support, stable - have been using it for more than 8+ years now and won't think of using any other framework if I am going to build something in Java. Very easy to design, build, deploy, troubleshoot, maintain REST API Services! You won't go wrong at all if you choose Spring Boot.
The bulk of my full stack experience has been dotnet. But for the past 5 years I’ve moved into Java and Spring. I’m not a big fan of the JVM, but it works. However, Kotlin + Spring Boot is pretty fucking incredible. 1.x is kind of a pain in the ass, but 2 and 3 really blow me away at how well things integrate when you go with convention.

The major gripes I have is that _up-to-date_ documentation on the best ways compose a modern app using several modules where you aren’t fighting the tools. Official module docs tend to breeze over stuff. I can’t tell you how much time I’ve wasted rolling my own ____ and fighting against autoconfig then finding there’s a simple but poorly documented way of doing what I need. But even with that frustration, I feel like I’m able to launch projects faster using Spring Boot than dotnet.

So far the responses have been lukewarm to negative. I'm all for a framework as big as Spring, blazing the trail to use Virtual Threads! I haven't used Spring Boot in a few years, and Webflux was a paradigm shift that didn't interest me. It'd be nice to see what this does for the framework.
Where can we read about the responses?
sorry, I meant the 8 comments here on HN. (there are quite a few more now)
I just wish there was an admin generator like django's
How’s performance? Is it better? Any benchmarks?
I wonder if there will be any tools to detect pinning or synchronized use to help with the virtual threads transition.

Blockhound I have gound useful for detecting blocking in non blocking areas.

There is a flag you can pass to the JVM to emit a log when a thread is pinned. It can also log a stack trace to show the call stack.
Moving the needle. But still as bloated as it can get.

Never was invested in anything beyond Java SE, now I'm never going to invest in Java SE again.