The Modern Java Platform – 2021 Edition
jamesward.com
jamesward.com
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 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.
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.
Sounds like the ecosystem didn’t get much better.
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``.
(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.
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.
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.
That's most non-toy desktop software
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?!
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.
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.
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.
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.)
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.
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.
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.
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.
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 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.
Spring is not the cure, it's the disease.
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.
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.
* https://www.axios.com/trumps-timeline-always-two-weeks-15133...
* https://www.bloomberg.com/news/articles/2017-06-06/in-trump-...
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.
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.
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.
:/
The Spark framework is so light and refreshingly comprehensible. I can actually tell what's going on when a problem occurs.
Spring even needs a web app to help configure its pleothora of behaviors.
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.
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.
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.
Couldn't be more happier.
That will be great service to this world. Considering endless turds Spring/Boot generates at runtime, bitcoin mining might most ethical thing to do.
- 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.
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.
true
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.
Java is not Spring Boot.
For example, this is Python 3.8 compliant runtime on top of Graal - https://www.graalvm.org/reference-manual/python/
You can also compile your application into a native image (like Go?) - https://www.graalvm.org/reference-manual/native-image/
you can try it in the next 5 mins
1. docker pull ghcr.io/graalvm/graalvm-ce:latest
2. docker run -it ghcr.io/graalvm/graalvm-ce:latest bash
3. gu install python
4. graalpython -m venv myvenv
And of course well-performing (but worse than JIT) AOT compilation is also a possibility with it, though I feel it is not needed as often as people think.
There's also project sulong that tries to bring LLVM IR into the graal ecosystem. Giving you the ability to integrate C/C++/Rust/Fortran/etc. Whatever has an LLVM backend could possibly run side by side with Java/javascript/python all without major FFI penalties and possible cross language optimizations.
Graal instead is a bit more complex to describe, since it incorporates many things. Perhaps the most important part of it is an abstract syntax tree-based interpreter, which can be used to implement a dynamic language with ease, and Graal can basically convert such an interpreter to a language runtime that uses the many many advancements behind the JVM like advanced JIT, GCs and the like. Such an implementation for small languages can easily surpass the “host” runtime in performance, for example R, Ruby.
What makes graal even more interesting is that this intermediate AST is language agnostic, it basically maps the guest language to JVM built-ins — this allows completely polyglot code bases (with JIT compilation between boundaries) and even has an llvm ir-based interpreter because dynamic languages often have C-based standard libs (and since it can inline between deps now, it may be faster than native FFI). But the whitepaper titled One VM to rule them all could give a much better overview than I could.
Since it is primarily a JIT compiler, it can be used as AOT as well.
(Also, there is graal wasm I believe as well)
WebAssembly was designed for the browser. Users want their websites to load quickly, so startup time is crucial. Also, users want to browse sketchy websites, so security is also crucial. WebAssembly is more like an instruction set for a "fake" CPU. It's very low-level and is not ideal for running high-level languages like Ruby (though it's possible!).
On the other hand, GraalVM isn't constrained by these browser requirements. This lets Graal do nifty tricks so that highly dynamic languages - like Ruby or JavaScript - run as fast as possible.
But then they took the base and used the java to machine code capabilities to be able to do ahead of time compilation to machine code.
But the most amazing part is that if you use the graal apis to define an interpreter for a language (any language) then graal can generate a compiler from that interpreter. It will compile all that is known at compile time and leave to runtime what needs to happen at runtime. So essentially it does partial compilation.
Code in modern Java (lamba etc) -> build native Linux exe -> package as Docker image -> deploy in Google Cloud Run. All wiring from CLI so CI/CD friendly (next is to use Google Cloud Build). Since it's native, memory usage small and boot time negligible. Since it's managed, it auto-scales (up and down, to zero cost).
My complaints:
* Quarkus is very opinionated. I'm used to this coming from Google App Engine.
* Building _native_ exe is slow. Like 1995 Java slow.
* Scala support is limited.
If you're used to App Engine, you know exactly the dream I'm living in. Without App Engine's limitations.
I use both RxJava and Reactor, and I sometimes have a one page function that could have been an entire application in the past.
My manager once asked me if we can have a complex endpoint to page data from multiple collections on multiple MongoDB clusters (paging is for backwards compatibility with some clients) and do it efficiently. This requires sorting query results on each of the clusters, then merging the streams of data into a sorted stream and then do that paging manually on the resulting stream. You also need to deal with various error handling and retry requirements.
He asked this 20 minutes before our 1:1 and I had working endpoint before we started it.
Does it have steep learning curve? Sure. But it is totally worth it.
I recently began doing a side project and, for the first time in a while, picked Java over Kotlin. I mostly did so because most books on deep details of the JVM are mostly books about the deep details of Java.
Plus: Java is moving along at a fair clip these days. Records just landed in 16, Project Loom and Project Valhalla are coming over the horizon, plus lots of other niceties that've showed up lately.
A: They say it's coming over the horizon. What's the horizon, B?
B: That's the imaginary line where the land and the sky meet that you cannot reach however fast you go.
So I think many don't know how smooth java can be if used correctly. But that also makes me think if there are low hanging fruit / obvious stuff I also don't know that could be a boost to my productivity?
It also shows you the state of variables at various points alongside the code, which is basically the context i want to log anyways.
Llvm IR however has not been designed with inter language interoperability in mind or at least not enough. E.g rust has no transparant, complete, seamless and efficient interop with swift, go, c++, etc
But in theory llvm is just an AOT and both AOT and JIT can enable true interoperability which is why graalvm support both a jit mode and an AOT mode. However JITs enable better performance at least for any high level, GCed language than would an AOT.
Moreover, GraalVM through the truffle framework enable unprecedented language designer productivity. Through high level constructs the designers can be much more productive than in standard VM/AOT, which explains how with a few engineers Oracle has managed to reimplement Java, ruby, python, js and R in parallel in only a few years...
(By the way Graal has sulong which actually runs LLVM IR on top of the JVM)
See also graalphp: https://github.com/abertschi/graalphp/blob/master/results.md
Oracle has managed to create a language framework that enable unprecedented performance and polyglotism but there are no human resources allocated to actually tuning the implementations. Without such framework imagining one developer to implement a language and expect it to outperform the reference vm made by an army of people through decades wouldn't be a realistic expectation but now anythings possible. Though it would be nice if others companies than Oracle understood the extent of this technological leap. Actually there is only one: shopify which invest in truffleruby
https://pragtob.wordpress.com/2020/08/24/the-great-rubykon-b...
Openjdk is one of the only language VM to support multiple languages (with coreclr).
However the languages that compile to bytecode are not automatically interoperable between each other. Usually languages (scala, groovy, etc) have partial interoperability with Java and almost zero interop between each other (scala <-> kotlin, kotlin <-> groovy). Kotlin stands out by being the only language truly seamlessly compatible with Java.
However graalvm is next generation because it enable languages to become easily and seamlessly interoperable with ALL other platforms languages at once. Graalvm is also revolutionary for its productivity (its a framework for building languages) and for its easy to get performance.
Not sure I would agree with that - I think Groovy has at lest as good, maybe even better compatibility than Kotlin.
1) The kotlin standard library is the Java standard library.
2) kotlin feel similar with Java and has analogues to almost every Java feature e.g SAM conversions.
The fact that groovy is dynamic make much less suitable for hybrid code bases (half Java half X)
It didn't quite pan out that way, but back then OOP was "the future" and everyone sort of assumed that only the classic style of OOP will ever be needed in the future.
There are some exceptions (Ruby took off after Rails was launched, Python was adopted by Linux distributions and it also had some decent web frameworks such as Django), but there's a reason we call exceptions exceptions. It's because they're exceptional, they're not the norm. Plus in the internet age (I don't count anything pre-2000 as internet age, since dial-up wasn't something most people wanted to live through) it's a lot more likely for something with a great future to be adopted quickly.
I thought OpenJDK was TCK-certified to be Java compliant, and 100% Free and Open Source software. Is that mistaken?
> To label a custom JDK with the Java brand (which is owned by Oracle) it must pass the tests in the Technology Compatibility Kit, which must be licensed from Oracle for such purpose.
Right, except there's a special exception for OpenJDK and derivatives. [0] Apparently though this doesn't always work out. [1]
However Spring is something that has stood the test of time and is used in thousands of actual projects. And Spring itself emerge out of practical software development with plenty of competing alternatives (Guice, classic J2EE plus many others).
The mentioned alternatives in article: Micronaut and Quarkus are not much beyound the demo state, and frankly based on some questionable ideas: Compile-time dependency injection and idea of "cloud native" - that somehow things run better in the cloud if they are compiled into a binary.
Dependency Injection is for people who reject the clarity of composition.
Not according to my benchmarks. That may be true if you use the GWT Widget library, but if you code 'to the metal' using Elemental, GWT produced substantially smaller code than TeaVM when I benchmarked it a few years ago with the Bench2D (Box2D) benchmark. Maybe it got better since then, but I doubt it given the progress GWT's successor made. Using J2CL (the successor to GWT), the following Java class
public class Main { public static void main(String argv[]) { window.alert("Hello World"); } }
would actually compile down to just (JS)
window.alert("Hello World")
TeaVM doesn't support code splitting that Both GWT and Closure Compiler support, or cross-module code motion.
Java 16 - https://news.ycombinator.com/item?id=26477144 - March 2021 (276 comments)
I found this comment odd - it does take some time to get a modern Java dev env up and running from scratch. For e.g. you have to at least download/install gradle or maven after the JDK. And then the author goes onto to talk about the Testcontainers project for which it appears you need to set up Docker. And so on. Wonder if I missed the meaning there.
https://docs.gradle.org/current/userguide/gradle_wrapper.htm...
Generating the Wrapper files requires an installed version of the Gradle runtime on your machine as described in Installation. Thankfully, generating the initial Wrapper files is a one-time process.
If you have huge codebase in Python or Ruby, you'll rip your hair more often because it's riskier to make changes (dynamic language) according to your use-case :).
As for dynamic language projects, they should include a test suite to mitigate such issues, but often do not.
http://hotswapagent.org/ Is an open source alternative and covers about 90% of what jrebel does
I'm not the biggest Java fan, I can tolerate it. Recently working on a Python project I find I'm massively unproductive compared to something like Java or more specifically Scala as using Spark.
Not having strong types and limited type hinting. I have no idea what things are, is it a int, string, object etc, clicking through in an IDE to see code/docs isn't as great as it's hard for an IDE to inspect the code compared to strongly typed language.
Not having a compiler means I have to have unit tests doing what a compiler would do or finding out I have syntax errors or other errors at runtime. This massively throws of my development flow as can't lean on the compiler to find trivial errors. Rather than doing compile I have to hope I have a test case with high coverage to instrument the code to get a syntax error or I have to start / deploy it to get the error.
There's a great deal of issues with dependency management. Venv, Docker etc help with this but it's a bunch of extra stuff on top I now need to worry about and sometimes still hit errors.
The only time I find I'm relatively productive in Python is writing a short 100-200 line script to do one off tasks / sysadmin / devops type scripts.
The fact if I change an interface, rename a method I need to recompile I find a huge advantage as everything breaks and I worth through the errors one by one and once complete and no more errors I have confidence the refactor is complete and the compiler has verified it.
For sysadmin-like tasks, for most stuff you can use Babashka, which is a limited Clojure + some frequently used libraries implementation with very fast startup thanks to GraalVM. For the rest, Python/ Perl etc. will probably still have a bit better standing because of all the libraries e.g. working with SNMP.
I'm a huge python fan, and I don't like/use IDEs on my personal projects. But eventually I still gravitated towards typescript because it had typing and the compiler + unit tests giving me a huge boost in ability to extend my project beyond a certain size.
I think the threshold is around 50-100 files, but smaller code bases also benefits from the typing structure.
There’s a saying in strongly typed languages “make illegal states unrepresentable”.
Java doesn’t let you get all the way there but it’s better than nothing.
When you look at strongly typed languages with a good type system, often when you compile it just works.
Besides hello world, I’ve never written program beyond a few lines that has just worked in a dynamic language.
...I do like clojure and the repl workflow and how everything is data. Haven’t done much besides play with it but have a friend who writes clojure professionally on a large codebase and their feedback is most of the errors they now see wouldn’t exist with a type system.
Of course, you can write bad/ unmaintainable code in any language. I have seen such code in Clojure(Script) as well. But some languages really encourage bad code where other languages already feel like you are doing something wrong when you write bad code. (e.g. Using many atoms in Clojure, doing boolean transformations etc. just from the top of my head.)
Strongly typed languages in my experience don't help anything, make programming harder for everybody and the benefit is (for most software) questionable. Compilers should handle most types for us and only give us a hint something could be better, if we were more specific e.g. with a type hint. Humans should think about the problems not about deep implementation details (like integer vs float vs double), when not strictly needed.
Nope, it's not the only way. In practice you'd have type hints all over your code and a linter to perform static analysis. Of course this can be integrated in you IDE, so it's easy to do.
> Not having strong types and limited type hinting. I have no idea what things are, is it a int, string, object etc, clicking through in an IDE to see code/docs isn't as great as it's hard for an IDE to inspect the code compared to strongly typed language.
Again, with type hints you'll find your IDE does a pretty good job. Heck, Pycharm even gets it right without type hints sometimes.
Not that I don't see the value in compilers, but the case is not as clear cut as you expose, far from it.
> There's a great deal of issues with dependency management. Venv, Docker etc help with this but it's a bunch of extra stuff on top I now need to worry about and sometimes still hit errors.
Not sure what Docker has to do with Python here... I don't use it and we're doing fine. Venv is something you have to learn, sure, but that's what it takes to become productive on any platform, be it Python, Java, Go, Whatever: learn the good practices associated with it.
And while the dependency management is not stellar with Python, that's really not something to love from Java either.
Python is a toddler in terms of typing system support. Probably a 2 year old.
Type hints are very different to a strongly typed language with a good type system. Haskel as an extreme feels like algebra, you have a type a, you need a type c, you have a function a to b, and a function b to c, you just compose them together and from your a you have a b. Tools like hoogle can tell you the function to use for the given args to get to the final type you want.
Ignoring Haskel even Typescript can let you express things hard to express in Python with ADT’s etc.
Docker has nothing to do with Python but if you want to produce an artifact you can just run anywhere it solves the dependency issue in that the decencies are in the image and you don’t need to run pip install on a deployment server.
What if it's some kind of a String? Only a String starting with "_". You may define UnderscoreString. But now it's not obvious what this is. You have to go look it up either way. A compiler may stop you from passing a regular String. If you're very lucky it may even stop you from casting an obviously wrong literal. But beyond that you're probably out of luck unless it's a crazy language. Once everything is a custom type how is remembering all the types different from remembering what each function does?
I'm not sure how unit tests help here much either. Why would you come up with an example that breaks your code in a unit test but couldn't think of it beforehand? Unit tests are used just as much in environments with a helpful compiler. They mostly help stop new breakage affecting stuff that used to work.
I wouldn't write off checking at runtime. You can actually define exactly what it is and it's easier. You can do as little or as much of it as you want. It's not compile time but depending on your software you may be able to get fast feedback. This level of checking would not be compile time either way. If you really want to be sure you'll be doing this in your typed language too. You're only worse off if you'd really benefit from the simple stuff.
If wrong data hitting a function can cause multi-million dollar loses would a single person think the compiler is good enough? What if it can cause major data loss? Big embarrassment? Pretty clear to me one will only trust the compiler with bugs that don't matter in the first place.
Clojure is a superpower especially for large codebases because you can maintain your state and ship your adjustments from the editor to the REPL in development which is almost instantaneous. This works for production code also, if you need a very, very quick hot-fix or want to look at live data in the live database without copying possibly confidential data around. Of course, you have to be careful. With great power... and all that.
Yes, some of those things are not so specific to Java. If you only care about performance in micro-benchmarks Java would probably win but probably 95% of the problems in the real world are way more complex. Also, good luck writing correct multi-threaded code in Java vs Clojure. Clojure is uniquely positioned for multi-threaded workloads thanks to persistent datastructures, atoms, agents etc.
Go is what I'd call minimalist. COBOL or fortran are what I'd call archaic.
It’s archaic enough that everyone can get things done instead of worrying about esoteric new features.
Source: A couple of talks at FOSDEM.
Don't attribute to technology the outcome of political decisions.
I also used to get things done in TASM.
Maybe the people behind Istio, InfluxDB, Docker, Traefik, Terraform, etc also chose the “archaic” Go for “political reasons”.
You're implying that if a tool is archaic, things cannot be done with it (also not true).
You're also explicitly saying that the features James mentioned are esoteric, which is easily disproven by the fact that many mainstream languages have them nowadays.
Google seems really hiring clueless marketers lately. This article is indeed full of hilarious nuggets like above.
It uses Spring Boot, but comes with a lot of sane defaults.
Spring is increasingly Kotlin centric and the combination is pretty nice. There's a wide variety of other frameworks such as vert.x, ktor, quarcus, etc. They are popular but compared to Spring quite niche.
With Graal and Kotlin native, the whole space is becoming less JVM centric as well. E.g. Spring Native just went into beta and Ktor has been inching closer to working on the Kotlin Native compiler for a while now (still some missing pieces). Particularly for serverless, this is relevant due to reduced startup time. Overall performance is not significantly better though.
After that, in terms of being standard and widely-used, Java EE, now known as Jakarta EE. It has a comparable level of magic to Spring Boot; maybe some of it is done better, some of it isn't.
DropWizard is still going.
I suspect that a larger proportion of Java programmers are working on headless data-munging backend apps than, say, Ruby or Node programmers. Those kinds of apps often either don't need a framework at all, or need some more specialist framework. Hence, Rails-esque frameworks are less of a priority for the Java community as a whole. Which is a bit of a shame, because it would be great to have a really strong alternative to Spring.
However Quarkus and Micronaut are also quite appealing for small projects.
> Non-blocking / reactive is one of the central demarcating elements of “modern” vs traditional.