There's some great stuff on the JVM today, but Spring Boot is recapitulating all the problems of J2EE. Everything is extremely "decoupled" to the point that you have no idea where anything comes from or why, and just adding a new dependency to your classpath will radically change the behaviour of your application (oh, you added a dependency on a library that has a transitive dependency on the MongoDB client? Guess that must mean you want a MongoDB connection pool spun up and running).
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.
I've been through the whole evolution from Spring with XML configuration to the current 'convention over configuration' approach of Spring Boot.
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.
There's a next stage after annotations. The current thinking is to replace annotations with function calls. It makes more sense if you use Kotlin because Java is a bit verbose when you do this and in Kotlin you get to create nice DSLs. This cuts down on use of reflection and AOP magic that spring relies on and also enables native compilation. It also makes it easier to debug and it makes it much easier to understand what is going on at the price of surprisingly little verbosity. Kofu and Jafu are basically still experimental but work quite nicely https://github.com/spring-projects-experimental/spring-fu/tr...
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.
> Stuff just works with minimal coding and you customise it as needed (or not, which is perfectly valid).
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.
Right, but you can't customise the automatic config. It's all or nothing - you have to either use the whole autoconfig as-is or reimplement everything that it does yourself.
While nowadays I wouldn't touch Spring with a ten foot pole if I had the choice, back then (must have been mid 2000's as well), Spring helped me really develop as a software engineer; everything I know now from (Java-style) interfaces, contracts and unit testing comes from that one six month internship where I was left to my own devices to build an application similar to what Sonar is nowadays (or was a few years ago). I still have the code somewhere, so many comments <_<.
Looks like I came up the same path as you: XML in Spring 2, with Spring 3 adding lots of support for annotations. Once Spring 4 came around, I was... okay with abandoning _most_ XML, but some things just made way more sense in XML, such as Spring Security configuration.
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.
My thoughts exactly. I started with the XML flavor, and although I hate XML, it was (IMO) a far superior mechanism than annotations. All the config was in one spot. You didn't need to recompile to change it. So much easier, all in all.
One power of Spring Boot , non-xml based code is using an IDE like intelliJ for refactoring/navigation. XML is hard to refactor/navigate if you're dealing with 1000s of files.
You don't need Spring Boot for that. You can use plain Spring with annotation-based config, no XML needed. And your IDE navigation will work, whereas with Spring Boot it frequently finds the wrong bean definition because of all the dynamic magic (ConditionalOnClasspath, ConditionalOnMissingBean etc.).
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.
There's a very good reason why Spring is used so widely.
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.
> Complex enterprise apps are often complex because the use case and the environment is complex.
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?
From my experience, enterprise apps are generally complex because they are a combination of:
- 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.
> 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 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.
Enterprise as in "sold to enterprises" or as in "created by enterprises"?
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.
> 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.
agree on poor analytical skills.
top talent devs tend to work on front-office apps that generate $, and not the backoffice intranet type apps for internal employees and processes.
i think it is a function of compension difference as well and dev self-selection
Spring proper is (relatively) fine; you can make applications with it that are more-or-less maintainable. Spring Boot is very different. It's a write-only framework, and while businesses may have enthusiastically adopted it, it's new enough that they haven't (yet) had to pay the maintenance costs and realise how bad they are.
I agree with this, having recently gone through the exercise of creating some "onboarding" apps for new grads using Spring 5 and XML, then again with annotations. All the underpinnings for a non-Boot application are still there and mainly sane.
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.
Write-only in this context means that the cost of maintaining something vastly exceeds the cost of rewriting that something from scratch.
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.
WOF == you understand your software while you're writing/developing it but give it a rest for a Trump's "two weeks" and you'll have no clue what you did.
The "different tooling" you mention should be the Java language itself. Let's not kid ourselves, literally no one writes enterprise applications in Java without some kind of a framework, and lives to tell the tale/doesn't go crazy. Since Spring is a de-facto "official" framework to write Java apps in, we might as well clean it up for inclusion into the core Java language, with attention paid to transparency to tooling in IDEs.
> In a team with 100 devs, simplifying and unifying decisions is extremely important.
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.
I disagree. I can spin up a CRUD service with Spring Boot in an hour including validation, health checks, db migrations, API documentation and what not.
It lets me move fast while taking care of the boring stuff.
I have the same experience as you. I have used Spring Boot in different environments for 5-6 years (first complex global enterprise and later small startup). Spring Boot have been enabling me to be very productive. I haven't had any major issues that I can blame on the "magic" that Spring Boot provides.
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.
That’s all fine and nice, except when the magik doesn’t work and you’re ctrl-clicking for hours trying to figure out what darn annotation is breaking the whole incantation.
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.)
Luckily this doesn't happen all that often. Unfortunately this is gonna happen in every framework. Compromises is what you always get when reusing somebody else's framework/program/anything basically, because use cases are never 100% similar.
A good framework lets you gracefully progress from 100% autoconfigured to 95% autoconfigured, 5% customized to 90% autoconfigured, and so on. It does this by working in a consistent way, where framework-provided defaults behave the same as custom implementations of the same thing, and you can use the same tools and techniques to understand what the framework is doing as you use to understand your own application.
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.
Well, as someone else on this thread wrote, it does happen all the times you're not preparing for a presentation or a blog.
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.
You know what should be taking care of all of these things? Java! For a language that bills itself as the "enterprise language #1", it's abysmal in supporting actual enterprise features like you listed above in a lightweight fashion. Instead, a whole third-party framework has to be tacked on just so you don't have to reinvent the wheel. Java doesn't even support dependency injection, something that I would consider an absolute minimum for even a small project.
>Java doesn't even support dependency injection, something that I would consider an absolute minimum for even a small project.
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.
The real thing that is missing is a runtime annotation scanning API.
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.
I don't think there is a need to blur the line between a standard library and frameworks. Maybe @Inject should have been in the JDK, but on the other hand it can be added to any project easily and is supported by several frameworks.
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.
If Java had had first-class functions to start with, things like Spring would probably have never got off the ground. Up until Java 8 you couldn't even pass a reference to a constructor as an argument without defining a whole class to carry it around in.
But first-class function is a completely different thing than dependency injection (DI). Supporting functional programming is a an aspect of the programming language itself, while DI is part of the infrastructure.
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 ...
DI is a design technique, you don't have to use a framework to implement it. Reflection-based frameworks became a popular way of doing it in Java because doing it in plain Java is cumbersome, and that's due to the limitations of Java as a language.
I don't know about that. Dmitri Sotnikov, joint author of "Web Development With Clojure", worked on a similar app and didn't even need types or OOP let alone a monster like Spring.
Enterprise apps are super complex, frequently a lot more complex than startup apps.
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'm sorry, having extensive experience writing enterprise apps I have to refute this. Almost all complexity I've ever seen was rooted in infrastructure, not the inherent complexities of said enterprise.
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.
Just as a counterpoint, I've done a little bit of game programming and currently work on a tax reporting system (mostly). Both are full of random shit you have to know for no reason. The game random shit I've had to learn was often tied to very specific platforms at very specific times. The game stuff very often I had to delve into actual math + algos.
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 understand, I should have been clearer. My experience has been the opposite. The companies I've worked for had actual complexity to tackle. I guess I got a little defensive because I've encountered "game programmers are gods, everyone else sucks" attitudes before from forums and fellow programmers and projected that onto your response. Sorry.
No worries. The gaming industry has its own set of problems for sure, so I'm not claiming it's inherently better in any way :)
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 :)
Extensive experience writing enterprise apps in many different industries? To me it sounds like you're trying to refute a generality with anecdotal experience. Have you ever worked for a heavily regulated industry like healthcare or insurance? The business logic is heavily tied to the regulations which vary by state/country and can be quite... cumbersome.
That is true. I don't really refute that there are instances of complex rulesets. I've worked in some regulated markets, but not healthcare or insurance.
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.
This is absolutely not the case. Enterprise app developers (etc.) are not stupid. The domain and the business problem are often extremely complex and hard to understand.
My observation from my long career of writing enterprise apps is that frameworks are super complicated with many hidden variables, so when you apply them to a complicated business problem you end up with the square of the complications of both systems.
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.
I had this experience with the so-called "app servers" at our company. We also have a daily batch processing system has tons of operational support, db+serivice monitoring, a very useful web UI, logging split out, authorization system etc. It's built around common Nix concepts like files, pids, pipes, etc. Thousands of these batch jobs are little JavaSE programs that launch, do their business, record their progress and report success or failure. Some are python programs and behave the same way.
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.
Yeah your experience absolutely mirrors mine. Don’t even get me started on JPA; I’ve used exactly the same phrasing you did: when you use JPA, now you have to know SQL, JPA and JQL. It makes no sense!
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.
That`s not spring boot, that`s whole spring (and google guice, etc), starting from the idiotic XML for bean wiring, to equally idiotic anntations, and finally, after 15 or how many years, they realized that plain java can be used for object creation. What a discovery! Still, they introduced @Configuration bullshit, etc.
(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.
I'm no fan of regular Spring, but it offers some legitimate value and has relatively comprehensible behaviour. It doesn't break grep: when a class is instantiated by Spring you can find a reference to that class, and most IDEs can also follow those references. Adding a new dependency doesn't change its behaviour.
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.
Fully agree. I've inherited an insane desktop application that uses the spring XML bean configuration. Then there's classes used to configure the configuration. And three separate property class types to pass the properties into the context, only one of which will persist the properties. And factory beans to instantiate a different bean depending on the properties passed in. And different XML configurations for each subproject which somehow join together but sometimes complain about dependencies being defined in the wrong place... Total nightmare.
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?!
I’ve sometimes recreated the Dependency Injection in 200 lines, because I had an absconse error for an entire afternoon. Sometimes one has to fix the right problem (It was with Dropwizard not SpringBoot, but it’s a recurring theme in the Java ecosystem).
ah, I am promoted thus to reminisce upon my boolshitten youth spent vainly stabbing out crud most fervid and amorous to dump the vilest cores to jest with that naivest child me to know it better than to presume that my brittle logic could affect it so profoundly to do my bidding...
This is true in most languages really. It starts with people just wanting to speed up their development by turning common tasks into some macro, or annotation, or even library. But where does it end? It ends in a convoluted mess of dependencies, weird syntax, and hopeless stack traces.
So much work can be done with plain old Java. Or plain old JavaScript. Or plain old C + stdlib.
Agreed. Setting up Springs OAuth Client was so complicated and getting the configuration right was taking so much time. I replaced it with a simple Filter that just did the query itself. It easy to go to that filter and add whatever security logic you want (at least for me it is).
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.
But you and up writing just the bits you need so that you end up with a framework that serves your use cases, that you grew so you understand and unencumbured by the use cases of others. It'll make you more productive... but not necessarily the person you hand it off to.
I simply don't understand how people favor a stringly typed custom DSL in a reasonably typed language like java. Why is it better to write Spring annotations instead of actual java code? There is literally no upside.
Annotations are fairly typesafe. The parts that aren't safe are parts that Java offers no way to do at compile time (e.g. "are all the arguments to this constructor of types that are in this set of existing services?")
Also very lightweight and pleasant to use are Javalin and SparkJava. I've used them for all kinds of projects with great results. Basically a Java version of Python's Flask - small and concise but stable and performant enough for many (or even most) web projects.
Our team has had a lot of success with both of these too. The single architectural choice I completely disagree with though is their use of Static methods to hook things together.
Plain servlets, jetty, haproxy for TLS, jstl, postgres, apache dbcp. Add in whatever specialty libraries for the project and you can pull away with 1/100th of the dependencies.
Even servlet containers are the wrong approach. I have from experience it’s best to stick to libraries. You know where you are when you call a function.
The Servlet spec now includes annotation scanning by default now, which is unnecessarily complicated and slows down start up.
I read a comment once, saying that Spring is a lot like the COMEFROM statement [0]. You are in a class, but you have no idea how you got there, or how to get somewhere else. Your IDE is no help, because everything is "decoupled".
I’m just learning about the COMEFROM statement, this exactly captures my reluctance with very modern apps. I especially despise checking permissions or caches using annotations, because you are never sure they are applied. I hope one day COMEFROM is as forbidden as GOTO.
I used to really like Spark but the primary maintainer seems to go through periods of disinterest. I switched to using Javalin https://github.com/tipsy/javalin which was built by one of the top Spark committers a few years back.
I felt strongly enough about this topic to write a full blog post: http://sreque.blogspot.com/2019/08/the-autumn-manifesto-why-.... TLDR: so-called DI frameworks are really just frameworks for creating and consuming global variables and have very little to do with the actual principle in of DI.
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.
I disagree with that blog post. It may be technically possible to hijack the classloader mechanism to make instantiating classes do dependency injection, but it's not easy or idiomatic, and it's not good for maintainability either; a reader can't tell the difference between a global service and a value object if both are just "new Foo()".
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.
> Everything is extremely "decoupled" to the point that you have no idea where anything comes from or why, and just adding a new dependency to your classpath will radically change the behaviour of your application
What does "decoupled" even mean anymore? That sounds like the opposite of "decoupled" to me.
Everything is wired up using Dependency Injection. So you just state your claims and are given appropriate objects that may be created by some third party factory factory. It's really neat until it isnt.
It's there an unspoken rule about how much dependency injection is too much dependency injection? Like I can see how it could get bad, but how do you know when you've gone too far?
Probably as soon as you don't know why an object appears in your tree. I'm not at all an expert on Di as the only project I worked on that used it a lot, I was also responsible for refactoring it such that spring di was only optional by annotated constructors, because spring startup is atrocious and we wanted to use part of the application in lambda.
I've not used it since because the project I inherited suffered extremely from nih syndrome.
The logic seems to be that having X use Y if it's available at runtime is less coupled than having X hard-depend on Y at build time. But IME the end result is similar to a "distributed monolith": you haven't actually decoupled it, you've just swept the coupling under the rug.
As someone who loves Python/Django, dabbled in Ruby/Rails, learned a multitude of front-end JS frameworks (Backbone, React, AngularJS, and Ember), and currently works with Java in an enterprise environment, I just want to add two points:
- 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.
XML is not any more or less "turing complete" than yaml, is it? They both express static data structures. I suppose you can make turing complete languages that use the syntax of either one (XSLT is one; i'm sure there are others in YAML), although it gets pretty iffy.
I agree, static data structures are far from ideal. Practically speaking, there's not much more you can accomplish with YAML or JSON compared to XML, even if some people find the syntax more palatable.
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?
> there's not much more you can accomplish with YAML or JSON compared to XML, even if some people find the syntax more palatable.
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.
I didn’t mean to imply that XML was preferable to YAML. I meant that neither is Turing Complete, so using either for dependencies and config is likely to be cumbersome. YAML looks nicer, and if you’re not using a decent IDE, you’re less likely to mess up with closing tags, etc., but that’s about it.
This. I tried to bootstrap an web flux application without spring boot or kofu or anything and you have to move thorugh so many different classes to get to the appropriate bean for the handlers that I just gave up. Also multiple simmilarly named classes like ``WebHandler``, ``HttpHandler``.
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``.
I feel like spring got leapfrogged by plain javaee a bit; I can imagine that in early '00s Spring was big improvement over Java EE 1
.4 or whatever but modern Java EE to at least some degree caught up, JEE 8 and 9 being relatively pleasant to work with, along with Microprofile, while Spring feels more stuck in the 00s feel.
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.