I actually liked the idea behind the XML configuration when I first got to know Spring in 2007. With this you could decouple the composition of your application from the actual code. You could deliver a jar and the user could decide with his own XML which parts to use and which not, for instance if he wanted to use your software in test mode or if he wanted to use another database. Otoh, most people who were using Spring were just following the cargo cult and not using any of the freedom that XML offered, and then all the XML configs were just massive overhead.
Then came Spring annotations. For the cargo cult followers this must have been a big relief. For those of us who really used the XML application configuration, the decoupling of code and configuration was now gone. There was still the option to use XML but it was frowned upon by the community.
And finally there was Spring Boot. The advantage of Spring Boot is that if you just want all the default Spring choices you can have a full fledged service up and running in no time. This looks really nice in blogs and demo's. With 'convention over configuration' you only have to adjust the parts where you don't like the defaults. This might seem nice but if you're working on a larger software project this can quickly turn into a major headache. All kind of choices are made for you by conditional spring beans which you might not even know they existed, there can be complex conditions determining their behaviour that might change all of a sudden when you add a new jar to your classpath.
Spring has one big advantage though. It is so very popular and widely used that as a Java developer you only need to get good at this one framework and you will get plenty of job opportunities for years to come. And the reverse is also true for employers: just choose Spring as your framework and you will have no problem finding new developers.
Another trend is native compilation. Spring native just went into beta (uses the Graal compiler). That still relies on reflection but they re-engineered the internals to be more native friendly.
Spring Boot basically added the notion of autoconfiguring libraries that simply by being on the classpath self configure in a sane way. It's one of those things that makes the experience a bit more ruby on rails like. Stuff just works with minimal coding and you customise it as needed (or not, which is perfectly valid).
Compared to XML configuration, Spring has come a long way. Separating code and configuration is still a good idea with Spring but indeed not strictly enforced. @Configuration classes can take the place of XML and if you use the bean dsl, that's basically the equivalent of using XML. Only it's type checked at compile time and a bit more readable.
You can't though, because the usual pattern is that the classpath-based self-configurer imports a non-public class that contains all the actual configuration. So you can't customize or extend the autoconfigured version - you have to either accept it as is or replicate the entirety of it from scratch.
I first saw Spring Boot demoed by Josh Long at a Pivotal office in Toronto, and my first reaction was to wretch at autoconfiguration, since it was extremely apparent where it would lead. The team I was working with at the time were a hard NO on annotation-driven config, which I thought was extreme at the time; however, several jobs later, I saw the proliferation of Boot and the autoconfiguration cancer it caused. Some projects were explicitly re-written with a hard technical requirement to not use Spring Boot, and those code bases ended up cleaner and more readable as a result.
The current gig is steeped in Boot and I've just given up and instead tried to use TypeScript for anything new, simply to avoid the Spring ecosystem.
What's more terrifying is watching new grads and green developers use this magic trash and have zero concept of what's actually happening under the covers. When I say zero concept, I really do mean that they have no idea what the servlet spec is, let alone containers or reverse-proxies.
For many projects, DI frameworks are a giant cargo cult. Whether you use annotations or XML. More broadly, Java's obsession with "flexibility" (really, false flexibility) is a giant cargo cult.
If you're talking about webapps specifically... using XML in Java was not a cargo cult. It was the only option in 2007. Even if you didn't use Spring.
For a large ebay-sort-of backend with lots of REST apis to both front end and back end itegrations?
Bottom line, organizing and maintaining large codebases on which lots of developers are collaborating is going to be painful no matter what your stack is. There is no technical fix for overcoming all the dependency and coordination problems created by large, complex software.
As nothing is going to remove that cost from you, the best you can do it transform one of set painpoints into a different set of painpoints. The most dangerous choice is then the one where the painpoints are not well understood, even to the point where you think they aren't there. Trust me - they are there, lurking - waiting for you to start tripping over them.
At least with Spring there is a well understood approach with a large pool of developers and some accumulated wisdom. That's better than most alternatives for real world use.
OTOH, if you are building a small project with a small team, it doesn't matter too much which framework you use, just use whatever your team members are most comfortable with. If your intention is to grow into a massive project, then finding devs who have experience in your stack will matter more down the road.
Complex enterprise apps are often complex because the use case and the environment is complex.
E.g.: - integration testing and unit testing is required - transparently pluggable backends for message queues so that locally you can use SQLite as your pub/sub storage but in production it's Google P/S - standardized health check endpoints for all your apps
... The list is infinite. You can make a decision for each point or you can agree with the team that you're using whatever Spring provides.
In a team with 100 devs, simplifying and unifying decisions is extremely important. And Spring _works_. I don't like it either, but it works.
The alternative is: solving all the problem that Spring solves with different tooling. And no, you can't avoid dependency injection in a 700 kLOC medical application, because you need to test the hell out of it.
Uh maybe... the real question is where does that complexity come from. Is it intrinsic to the problem or just bureaucratic slob? Given a framework so popular, what are the incentives to go uphill and challenge assumptions - with the likely risk of being fired - or just concede and ad your little contrived contribution to the problem?
- environment: integration of a large amount of services - and a good amount of them are legacy and idiosyncratic
- use case: enterprise app are at the intersection of real life and the virtual world: the rules are messy, illogical and have a baggage of 20/30+ years. Thus they cannot be changed at all. This is IMO the main difference between a "pure" greenfield startup kind of project and the enterprise one.
- add another layer of burocracy and complex environment to navigate
And with that you got the enterprise app world :).
In the end whatever framework is chosen, the most important property is the availability of common language/patterns.
This is in my experience exactly the problem. They can be changed and should be changed as it would save everyone boatloads of time and money. Engineers need to advocate these business process simplifications, and managers need to illustrate the ROI of making such simplifications.
One has to challenge nonsensical requirements: it’s a critical part of engineering.
Both are complex, but for different reasons.
Software that is sold to enterprises is complex because they compete on number of features (in checklists), and there is no pressure for quality since the software users have little saying on what software gets brought.
Software that is created by enterprises for internal use is complex because the enterprises themselves are complex. They are full of rules, created by different people with very different goals, that add up with time, and the applications must deal with them.
Maybe it's just me and my scarce disposition for forgiveness, but after several years I tend to believe complexity emerges as a consequence of superficial understanding, diffuse aversion to analysis and outright pool analytical skills.
:/
Spring Boot absolutely yields write-once throw away, and you _must_ consider the source: Pivotal is a contracting shop, so it's in their interests to hook people into an ecosystem that they happen to be experts in. I'm surprised this fact is lost on most.
I don't know Spring and Spring Boot well enough to pass judgement, but this situation generally arises in software when you have a language or framework that's reasonably expressive, where the language has a lot of sigils or keywords and it has "shortcuts" that you have to grok before you can really understand codebases that use those shortcuts (like annotations or heavy/excessive use of design patterns). It leads to software solutions that are only well understood by the original authors due to their specific knowledge and problem solving approach and anyone who comes along later must have the exact same overlap of knowledge and skills otherwise they'll find it easier to start fresh. This includes the original authors if the time lapse has been long enough.
This often then leads to the next phase of miserable software jobs, the second-system effect.
* https://www.axios.com/trumps-timeline-always-two-weeks-15133...
* https://www.bloomberg.com/news/articles/2017-06-06/in-trump-...
I agree with this.
Unfortunately, Spring is often chosen for projects with much smaller teams too. Just 1 to 5 devs on what is fundamentally just a CRUD app. Wrong tool for the job.
It lets me move fast while taking care of the boring stuff.
Nothing to do with team size.
I have read startup experiences with other frameworks where they write blog posts about all the issues they had to spend time to fix, that is trivial to solve using Spring. There is a slight learning curve in the beginning, but it is worth it in my opinion.
Or you realize a that the tiny tiny small configuration change you need isn’t contemplated by the code supporting the auto-magik, so you start adding overrides which turn off the autoconf, and you have to manually configure the whole beast by yourself (discovering all the undocumented gotcha’s along the way.)
None of that's true of Spring Boot. The autoconfigured stuff comes in in its own fashion that's hard to relate to your normal configurations, and it's encapsulated in such a way that as soon as you want to replace part of it you find you have to reimplement all of it.
If you're building something marginally different from the routine the curtain is drawn, and IMHO you're probably better off buying a shrink-wrapped ready-made SAAS.
For small projects, DI is easy done by passing things via the constructor. If multiple things need to get wired together, pull that wiring logic out into its own class or method (FooBuilder.buildDefault()). If things start to get tedious, that's a really good time to stop and reflect on the design choices. That stop-and-reflect opportunity if often lost when things can be simply AutoWired together.
Spring does this my examining the class path, casting it to a urlclasspath, finding all the zip/jar files, unzipping them and then parsing the byte code.
Interestingly the set let spec added annotation scanning too. And now if your not careful you jar files get extracted three times. The JVM, the server conteainer and spring all repeating the same work.
No wonder people think java is slow to start up.
In general I think, it is wise to keep the standard library small, because innovations are easier to implement in libraries. Rust is a good example for this style.
I agree with you, that Java should have had first-class function from day one. Ironically this was considered by the language designers, but they decided that it would be too exotic for the average Joe. OOP was a hype back then ...
Spring is not the cure, it's the disease.
Someone basically comes with 1 thick tome worth of business knowledge plus 1 thick tome worth of legal restrictions and you're supposed to codify all of that, to the letter, in software. Some of the business logic can make you cry.
Software dev: "But that's not clean/elegant".
Business owner: "Reality doesn't care about elegant/clean, it cares that we build stuff our customers want and that won't get us sued, so we have to implement it to the T".
I would even go so far as to say that the enterprise logic was so straight-forward that most programmers spent their time inventing problems, which is how look at the enterprise market. They's a lot of "inner platforms", meta-problem solving and needlessly complex deployment environments.
Contrast this with my current industry, gaming. Here the problems are real, tangible and hard. Suddenly overcomplication is much less of a problem, because the extra cognitive load becomes too much when your core problem is already hard.
The tax reporting system I get bogged down and mired in the literal thousands of edge cases and exceptions due to the interactions of all of the laws, sometimes written maliciously by some political entity, or sometimes some local municipality goes against the federal laws leading to literally impossible scenarios. On top of that you have things like your company booking transactions one way that they shouldn't have and is now too much of a pain to change so it has to get reported differently at the reporting layer (which itself is a problem as to why this wasn't noticed initially). Very often the gov't specs themselves are contradictory.
Here is an example: stocks can sometimes pay debt interest did you know?! That doesn't come up in any of the fixed income documentation I had to read. But right there in some list of bonds the IRS publishes every year are non-bond things. wtf?! So do you fabricate some new type "debt interest paying thing" of which are mostly composed of bonds and once in a while stocks in your data model? Do you keep your data model clean and fabricate an internal bond to represent the debt part of the stock in these cases and link it to the actual stock? You will have to consult with your legal/tax department and realize if you were to misreport this income what would the resultant fines and loss of customer goodwill is (due to higher chance of them being audited, paying more tax etc)? Do you conclude that your internal team cannot keep up with the ever marching and changing tax law (FATCA anyone wtf?!) and outsource this to a tax reporting company? The scope of our tax reporting is nearly unlimited since the US government seeks to capture all of human endeavor. The results of getting this wrong are very real. People get audited and lose money and sue our company and can go under due to litigation costs. In games, if we fuck up, our company goes under and everyone loses their job but I didn't have to worry so much about potentially ruining the jobs of people outside of our company.
This is actual complexity your software will have to deal with no matter your language/platform.
In my, admittedly limited (never AAA), experience with games I always seemed to be bumping up against physics (time, memory, latency/speed of light) but with enterprise apps it's almost always bumping up against the sum total of human stupidity past and present.
I'm glad to hear if your major challenge in the enterprise was to solve actual problems and produce real value. My journey was mostly learning one over-complicated framework after another, and finally coming to the conclusion that it was mostly for nothing. I was the local Spring expert, but at the same it's biggest critic. The "code", or more accurately the configuration, gets very consise, but you risk ending up with only a handful of people in the building who know how to debug the app properly.
I started coding Java when Sun marketed it as a very pragmatic choice, focusing on "simply writing code". The influence of IBM and the whole JEE movement (including Spring) still looks at the problem of coding from the wrong angle IMO.
Creating a good development environment is not about creating a all-in-one runtime environment or methodology, nor about simplifying the problem space for developers by letting them write plugins to large servers.
It's about establishing a fast RAD cycle and offering a buffet of good libraries, to simplify the writing of code.
Java used to be the language which allowed you to "just program", nowadays the mainstream choices are golang or node. To me it's become very clear that Java is on the wrong track here.
There are an infinite number of distractions as a coder, including Microservices, responsive design, functional purity, patterns etc.
If you managed to duck most of these and end up in a place where you were producing value effectively, you were very lucky judging by my experience :)
That said, I stand by the point that most complexity I've seen has come from infrastructure. Because although the rulesets might be complex, the way they are encoded is usually the source of the problem IMO.
For example, if the rules of the business are encoded in such a way that unit testing them is straight-forward, it puts the business at the center.
But when every rule is testable only in a complex deployment, the complexity of the app is no longer tied to the complexity of the business.
And the latter has been more the rule than the exception in my experience.
We ended up in framework hell. In response, we ditched all the frameworks we were using, went to pure JavaSE, and ended up with a (much!) faster, more reliable enterprise application that was far, far easier to maintain.
Our JEE apps were deployed to these app servers with very little internal support or expertise that very often had devs having to physically log onto production hosts and figure what in the world was wrong, restarting the app server, grepping logs and in general having to learn about these app servers (and usually just shrugging their shoulders at what went wrong). The sum total of things you had to know to keep the JEE deployments up was as much as you had to know about Nix processes PLUS you still had to know all the Nix stuff, except none of the JEE infra was built out since the app servers were just these monstrous processes with hundreds of db connections and thousands of threads.
My team long ago ditched JEE style deployments and have our apps all managed with the batch processing system and have been none of the better for it. Some* of those apps use Spring/Boot but I we've been doing a decent job in the code reviews of just rejecting anything too auto-magikal. Other teams that have stuck with these massive app-servers have stagnated since the deployments are so frail, no one dares make large changes. This is mostly an institutional problem but still the end result was sticking to simpler JavaSE stuff has led to way more productivity.
Enterprise developers aren't stupid but enterprise software is some of the worst software I have encountered in my career. When a development team has one captive customer you get the results you would expect.
(There are cases when such features may be useful, e. g. systems that are extended by 3rd party plugins, like Maven, although may be done without that as well).
For normall applications that´s nothing more than bloat and limitations. The problem that the majority of users follow cargo cults without understanding what are they doing. In result the ecosystem is full of bad practices.
AbstactArgumentBuilderFactoryFactory were in fashion for the same reason.
Otherwise Java is a neat little language and a good platform.
Spring Boot is real a step change in comprehensibility, in the wrong direction. It's on a similar level to adding COME FROM to the language. You're not wrong to complain about Spring/Guice in general, but implying that they're remotely comparable to Spring Boot is thoroughly misleading. It's not the same thing at all.
> Spring Boot is real a step change in comprehensibility, in the wrong direction.
With VMWare's Tanzu crap for containers and Spring native initiatives, the idea is complete lock-in in VMWare ecosystem from developer desktop to running service. The goal seems that no one should have any visibility on their own systems except VMWare consultants.
But what exactly about stock Kubernetes and the fully-OSS-for-nearly-two-decades Spring Project strike you as "lockin"?
This is a bit like accusing Red Hat of lockin for shipping a Linux kernel.
It's basically impossible to know what will be instantiated, extremely hard to inject and configure things. When I find the time I'm going to just create things directly from Java with constructor injection and ditch the whole sorry mess.
Edit: did I mention how unbelievably slow it is as well?!
So much work can be done with plain old Java. Or plain old JavaScript. Or plain old C + stdlib.
There is a balance here that might be hard to get right. Spring and other frameworks make things easier until they dont. At some point it might be easier to just write code instead of configuring your way through these frameworks. Many times I rather write code then go configuration hunting.
What I do like about Spring is that they offer a lot of hooks and interception points to overwrite with your own logic.
That's most non-toy desktop software
I'm happy to see so many years later it is still alive and kicking and powering so many systems.
We definitely need more of this.
I spent so much time this week chasing magic buttons in that over engineered piece of stink.
I'd rather do raw HTTP servlets at this point.
The Spark framework is so light and refreshingly comprehensible. I can actually tell what's going on when a problem occurs.
Couldn't be more happier.
That said, I think the jvm and java the language are in a great spot. It's the frameworks and community that need a shift in mindset.
DI, in the sense of separating the instantiation of long-lived service objects from the classes containing business logic that accesses those long-lived service objects, is a great thing for testability and maintainability.
Autowiring mechanisms where you have some kind of global bag of (pseudo-singleton) services by type, and wire service dependencies implicitly by type rather than explicitly, are a legitimate tradeoff that's appropriate for some cases.
Don't conflate Spring with Spring Boot. One is a framework that offers some legitimate value even if it makes some questionable tradeoffs; the other is a fractal of bad design.
"DI, in the sense of separating the instantiation of long-lived service objects from the classes containing business logic that accesses those long-lived service objects, is a great thing for testability and maintainability."
You don't need a DI framework to do any of what you described. Also, I believe that what you are saying doesn't fundamentally describe DI, though it is related. In this post I go over what DI really means: https://sreque.blogspot.com/2019/09/dependency-injection-101...
I have never seen a case where using autowiring forms a legitimate tradeoff; it has always resulted in worse, harder-to-maintain code with little benefit in return.
I conflate Spring with Spring Boot because Spring Boot is built on Spring and most of my problems with Spring Boot apply equally to Spring.
Architecture astronauts will produce the same designs regardless of the programming language.
What does "decoupled" even mean anymore? That sounds like the opposite of "decoupled" to me.
(/not a Java programmer).
I've not used it since because the project I inherited suffered extremely from nih syndrome.
- If you're going to have config/setup files, make sure they utilize a language that is Turing Complete. YAML looks pretty, but for all practical purposes, is it really better than XML?
- I've said this before, and I will say it again: I doubt writing an import statement ever killed anyone.
Edit: the two points are related. Having a Turing Complete config/setup file makes it easier to add a level of indirection between your code and library/framework code, so you can e.g. utilize different implementations for different ENVs.
Also, trying to shoehorn conditions and loops into a non-Turing Complete language is cumbersome. I would imagine it being a nightmare for platform and framework maintainers, as well. Rather than create a config or dependency DSL for every platform, why not just put the language to work for you?
Sure, and vice versa.
I think there are arguments to be had for whether you want your config language to be "turing complete" (or in general capable of containing logic or just static data). I am not sure I am convinced.
But you seemed to be saying that XML was preferable to yaml for some reason related to turing completeness/logical power, which I'm not seeing. You can "shoehorn" conditions and loops into YAML or XML if you want (by defining a semantics on top of either one), and it's going to be cumbersome, yup.
Spring even needs a web app to help configure its pleothora of behaviors.
I don't have much other experience so I thought it was just me.
DI is also a pain and you never know where something is comming from unless you are familiar with what every single functions adds to the ``CONTEXT``.
Take a look at https://github.com/spring-projects/spring-boot/issues/25742#...
I included a workaround allowing to opt-in for autoconfiguration instead of opting out. I have used the filter for more than 2 years without issue.
That will be great service to this world. Considering endless turds Spring/Boot generates at runtime, bitcoin mining might most ethical thing to do.
true
Sounds like the ecosystem didn’t get much better.
I swear I'm going to add a bitcoin miner to my libraries and list it in spring.factories so that it autostarts whenever someone starts a Spring Boot application with my library on the classpath. There'll be an undocumented config property to turn it off or make it mine to a different address. That's standard practice with every other library that uses Spring Boot, so it's perfectly ethical, and it's not like anyone using Spring Boot would notice that their application was wasting a bunch of resources.
Maybe someone else did this already. That would explain a lot actually.