The jvm is a pretty insane beast. It will do usage based recompilation, escape analysis for memory, so non heap allocation is super fast, has great memory safety...
But a lot of people use it with spring/spring boot, a technology designed to work around the complexities of a particular type of middleware software in the late 90s and early 2000s. It's cargo cult programming of the highest order.
In the OP, the author is comparing apples with oranges, with a bit of misunderstanding that java/jvm means spring boot, and while that is true for a lot of people and certainly a lot of stuff on the internet implies that 'this is the way', it's not required.
Startup times of ~100ms are absolutely standard for a big program, similarly unit tests taking 1ms.
I prefer to write kotlin rather than java, as it's a nicer language ,IMHO, but still those bytecodes run on Jvm and same stuff applies.
Edit: im not advocating writing 'ls' in java, and I would also agree that java uses more memory for small programs, so its not a systems programming language probably.
I think we can sum it this way. The blog post writer's intuition was correct: if you write two equivalent Go and and JVM programs, the Go program would use less heap memory and have faster startup times. What they are incorrect about is the extent of these claims.
It is obvious that most of the memory and startup overhead in their software comes from Spring, rather than the JVM. The JVM is probably not an ideal platform for writing an Kubernetes infra tool like operator or a sidecar (mostly due to heap size and more a complex binary packaging story), but using Spring Framework for writing these kind of tool is a bigger problem. If they just wrote their initial tool in Kotlin without using Spring framework at all, it would be much faster. They could even have kept Dependency Injection with a lightweight framework like Koin or Dagger (even a reflection-based framework like Guice performs worlds better than Spring).
I would probably still prefer Rust for writing most infrastructure projects nowadays. GraalVM Native Images has similar memory footprint to Go and solve the slightly troublesome JAR deployment story, but with the complex license terms for the enterprise edition (where most of the performance optimizations are), I don't feel safe using it. I also prefer Rust's concurrency model and I find cargo to be a far more pleasant experience than either Maven or Gradle, but in the end of the day, you will not see serious issues if you use Kotlin in a sane way, and you can even save on some Go boilerplate.
Rust has its uses but why would you write infra code in Rust when Go is used for most of it, and is just much more ergonomic and fast to work with. The iteration times with Rust are quite detrimental. On the other hand, most of k8s' ecosystem is in Go.
I don't like commenting in language-war territory things but I found your comment surprising. "Rust or JVM" for infra isn't a dichotomy I would expect.
They didn't have particularly great reasons to migrate at the time besides the team wanting to write rust. That article is just trying to provide validation
"With the Go implementation, the Read States service was not supporting its product requirements. It was fast most of the time, but every few minutes we saw large latency spikes that were bad for user experience. After investigating, we determined the spikes were due to core Go features: its memory model and garbage collector (GC)."
The entire point of writing rust is that you're trading writing speed for runtime safety. It takes longer to write but you can guarantee you're never going to have a nil pointer panic or race condition.
Unless you're doing something super weird your infra code is gonna be running for way longer than you spend writing it so who cares if it takes an extra 20%/30%/50%/whatever time to write?
Eh, I don't think iteration times in Rust are nearly as bad as you think. During a realistic development loop compilation is <1s in most situations thanks to incremental compilation and tests are fast unless you have synthetic slowdowns such as sleeps or IO (which would effect both languages equally).
At work we work on a Java code base 20 years old and it is written like C. No dependency injection or other Java Web development like shenanigans. Almost every lib etc has been built in-house. It runs an MMO, it's fast. Its just way more productive/faster to work and implement something in the Java codebase than a C++ codebase that we have.
Someone shared a Job posting which asked for "no java experience". It was funny.
If you write Java just like you would write Go - meaning, no reflection shenanigans, use Jigsaw to cut out unneeded JVM stuff, or use GraalVM and compile natively (which you will be able to, because you did not use any of JVM reflection magic), Java will absolutely be competitive.
In the end it all boils down to higher-level logic compiling down to machine code that has to run. A language's culture and philosophical choices are tied to the language itself as much as syntax or compiler, so maybe the largest difference between languages like Java and Go is really the whole developer attitude and aesthetic choices.
Java developers may write verbose python, but that doesn't compare to the crimes actual python devs commit.
Currently trying to modernize a python project that doesn't use modules, just executable python files that import each other with custom sys.path hackery, which is also used for globals, no type annotations, GLib used for everything including math and string to int parsing.
Hey, sorry, that might have been me. I did that then left the company.
In my defense, I had to hack around a different Python library also manipulating sys.path, which nobody likes except this one dev team in a different timezone. They somehow got a director to declare that I would fix this issue they self-created before they woke up in 8h, and I wasn't allowed to rip out the library. So, ugly sys.path manipulation in the exact way that library wants. Not proud of it, but it sounds like you were given time to engineer an actually correct solution.
The project is an open source project, so it's likely not your fault :)
I can fully see how and why this might have grown historically in ye olden days of python2, but it's not sustainable to continue adding floors ontop of a rotten foundation. Code needs maintenance like anything else, and far too often there's no budget or time available for it. Even if that maintenance would reduce the overall workload.
That is funny, because I was a Java developer for many years, then Scala for a few years, and these days I mainly write Python, but the last thing I go for is creating a class. That's generally only when a set of functions in a single responsibility module need to share / mutate some state, and it's more self-documenting than passing around dictionaries.
Java is really good. Java developer culture is awful.
If you instead of spring boot just pick a few dependencies you really need, you don't throw the whole Design Patterns book at it just because you can, and you don't try to make everything changeable without recompiling or redeploying, it's pretty nice to work with
I have said that for awhile: the worst thing about Java is Java developers.
I have some issues with the Java language (though Java 21? Actually pretty ok!), but there's no question that there's a lot of great stuff in regards to libraries in Java land.
A lot of the stuff that's just built into the JDK is already very good, for example. NIO can be a bit hard to work with, but is generally very good, fast, and reliable. A lot of the concurrency abstractions (e.g. BlockingQueues) are really pleasant to work with for most concurrent programs, the different types of mutexes/locks give access to most of the patterns you want, and the thread-safe collections like ConcurrentHashMaps are very boring, in that they work pretty much exactly as I want them to.
If we extend to third party libraries, it's even better. Vert.x and Disruptor, for example, are downright excellent tools for wrangling concurrency.
The issue is that it feels like a lot of Java developers are stuck in 1999; it can be like pulling teeth to even use NIO, which isn't exactly "new" at this point. When I wrote some stuff using BlockingQueues instead of throwing `synchronized` everywhere, people acted like I was grabbing this was some exotic code from a distant land that had never been tried before, When I imported Vert.x into my project because I needed to run a lot of non-blocking concurrent tasks, it required a lot of justification for using it instead of using threads everywhere.
Agree with everything that you wrote. The saddest thing is the job market. It would not be a lie to say the 90% of the jobs list Spring/Boot as a requirement. It's like a framework equals the whole language. This cannot be a good sign. I'm thankful enough to have around 2 decades of Java experience without touching anything named Spring and boy I can tell you I have been having lots of fun with Java. Using Vert.x right now and I like it a lot.
I don't do web stuff (and hopefully that can stay that way), so I've managed to avoid a lot of Spring stuff, but I am stuck using Spring Streams for some Kafka stuff I'm doing. I'm not going to lie, I don't love it, but that's mostly because it feels like all I do is write YAML files all day.
Vert.x is a lot of fun, and I've gotten pretty decent performance with its SQL libraries in particular, though I'm not sure I'm a huge fan of the EventBus. I've had better luck with LMAX Disruptor, at least for the data-processey stuff that I do.
But as I said, there's a ton of really great libraries in Java. The language isn't perfect, but Java 21 fixes a lot of my gripes, and it's nice to have a ton of really well-tested libraries to minimize how often you have to reinvent the wheel.
> The issue is that it feels like a lot of Java developers are stuck in 1999
Where are you working that you encounter these people? I've been doing Java for about 15 years now (C++ before that) and as the years have gone by I just don't encounter this stuff. I do work in finance technology, so we're not doing crud apps, maybe that makes a difference. Granted, there is a lot of "C in Java" code around in this industry.
> When I imported Vert.x into my project because I needed to run a lot of non-blocking concurrent tasks, it required a lot of justification for using it instead of using threads everywhere.
Isn't async unnecessary now? Threads are now cheap enough that its much easier to write sync'd calls on a thread.
I think Go adds to the enterprise culture and it does so on a global scale. Go is all about a consistent code style and a reduction of language complexity where your hands are tied on how you write code.
I haven't touched it since it was still called "AngularJS", but I remember AngularJS kind of felt like they were trying to make it so you write "Java" in "JavaScript". Ember too, if I remember correctly.
Like the article I hear Spring Boot here mentioned again. I also really hate the annotation culture. This is big in Spring Boot, and more common in Java since it is so damn verbose.
It is not inherent in Java though, and the Kotlin "developer culture" seems to be much more annotation averse (as we all should be).
You do realize that Java is objectively less verbose than Go? Even on a vanilla language to vanilla language basis, but let alone against something like Spring Boot that does almost everything for you in a typical CRUD application.
Annotations are declerative shorthands. How is a trivial spring boot endpoint with methods with a single @GET line above them denoting the endpoint verbose?
What about a single SQL query in an annotation above an interface method's name? Will your whole implementation of connect to db, execute query, iterate over the resulting rows, and convert them to some native object/struct shorter than.. 2 lines?
> You do realize that Java is objectively less verbose than Go?
I might agree if you're speaking about Java since ~11 or so.
One of my biggest complaints about Java, especially before the lambda syntax caught on, was how many extra files it made me create. I felt like every project had lots of wrapper classes, and people would make a make a file for each class.
You could argue that that was "bad" Java and they should have nested the classes, and that's fair enough, but it certainly wasn't uncommon Java. It artificially makes the language feel way too verbose if you have ten files more than would be necessary in a comparable language.
Once people started to embrace the lambda syntax, I feel that Java got immediately more fun to write; you weren't constantly creating one-off instantiations of interfaces or classes (well, at least not explicitly anyway), and the language felt considerably more streamlined.
Java 21 is actually one of the most pleasant surprises I've had in quite awhile in the tech world, in that I'm actually having fun writing Java. Sealed interfaces and record patterns make some code considerably more pleasant, and I actually have been writing Java in my free time, which is something that I would have said would never happen if you had asked me five years ago.
> which is something that I would have said would never happen if you had asked me five years ago.
I think a lot of people are noticing the changes Java has had in the previous years. The language has made a lot of improvements, and I feel that the mind set of the community has changed. The old enterprise way of factories and unnecessary abstractions have lost a lot of popularity, and is mostly still alive in legacy software/teams and universities who have not yet caught up.
Even Spring Boot is now a valid approach for getting sh*t done for startups. There are of course frameworks that are more light weight, or you can start from scratch and choose your own libraries to keep the size down. But SB is simply good enough for most use cases, and even supports native compilation now.
I still am not a huge fan of the Spring stuff, I have to use Spring Streams for work and I think it’s unpleasant to work with. It seems like the rest of the world has much more fun configuring YAML files than I do. I had to use Spring Boot at a previous job and it wasn’t for me, but honestly I really just hate working on web stuff.
But that’s obviously not the language’s fault. There are frameworks in Java that I think are great, like Vert.x; hell even going super low-level with NIO is straightforward enough if I really need control of HTTP stuff.
The stuff I really have the most fun working with is concurrent and distributed programs, and I think Java (or at least the JVM) is pretty hard to beat with that. Vert.x, Disruptor, and even the built-in JVM concurrency libraries (other than synchronized) are excellent; they have a Just Works quality to them.
And nowadays, GraalVM is good enough with its native compilation that you can avoid the long startup times and keep the memory under control, so it even is reasonably ok for custom command line tools.
Sometimes --in Spring Boot-- they hook different systems up with eachother: this makes the framework very "magic". I really dont like that: I want to be able to CTRL-click my way to understand how everything works!
I think this is why most criticism of Java sounds weird to me. I've used Java since 1.3-ish and I've never used a web framework or an annotation and I think I've used a factory once (I think there's one in Swing and I recall thinking it was a stupid design choice).
"Coupling by annotation" culture (which diminishes many of the benefits of using a typed language, as it pushes coupling to runtime and by means of reflection) IS inherent to SpringBoot. Hence my distaste for it.
Annotations were adopted to cut down on the massive amounts of XML that was used to configure everything beforehand, still a lot was loosely wired for "extensibility" and people have learned that it was 99% overengineering so that's cut down these days in various ways.
At $CURJOB, we have built a pretty good business on an app built in Java. We don't use Spring, but instead a lightweight MVC of our own creation[0]. It is open source but I don't know if anyone else is using it :) .
I once asked the founders why they chose java; it was because it was 2015ish and they had an existing product in java and they knew it. Which makes sense to me. I think there's a lot of path dependence to language choice.
I also think the domain matters. For database based webapps, Java can be great. Lots of tooling and knowledge around it. And modern java is pretty friendly to write. Plus, if you are interacting with something over an API and deploying it via a container, who really cares what it is written in?
For kubernetes operators? Seems like a natural fit for golang. Anything kube really. I had a friend who ran a k8s consultancy for a while and said that they'd prototype stuff in python because it was easy, then implement in golang because that was what was consistent with the rest of the ecosystem.
Spring boot is just a tool. People seem to forget and take for granted the knowledge that is hidden there. Sure you can wire everything together yourself, but who does that? It would be such a waste of time. I rather wait these 5 seconds for a long running service, tbh.
"PayPal and Wal-Mart have also had high-profile switches to Node.js. Of course, they’re comparing two completely different things to make Node.js look better. In these too-good-to-be-true stories, they’re switching from a gigantic enterprisey codebase to a Node.js app written from scratch. Is there any question that it wouldn’t have been faster? They could have switched to pretty much any anything and gotten a performance gain.
In LinkedIn’s case, they had proxies running on Mongrel with a concurrency of 1. It’s like switching from using one finger to type on a QWERTY keyboard to using ten fingers on a Dvorak keyboard and giving all the credit to Dvorak for a better keyboard layout."
P.S.: Google became so awful that a search for even the direct title of the article does not land it in the top results - just references to it.
Like, what other option is there? There is either a proper, battle-tested solution which requires some configuration so that it works as you want, or you start from scratch and create something specifically for your own usecase.
In the latter case, it may actually mean a significant amount of development orders of magnitude more than looking up how to configure stuff, constant maintainance, etc.
In Go, people will write code to use the standard library for the app they are developing instead of pulling in a framework to do the work for them. Most Go developers have a culture of minimizing dependencies to utterly essential ones that they cannot write on their own.
In Java, people will pull in a 100MB+ mega-framework for a hello-world REST service. Oh and another 50MB for ORM. Another 25MB+ for nailpolish, etc.
The extreme difference in basic developer culture causes visible differences in performance outcomes. Can't even blame the JVM - it is a superb beast that is overloaded by Java developers putting Mount Everest atop it.
> In Java, people will pull in a 300MB+ mega-framework for a hello-world REST service. Oh and another 200MB for ORM. Another 250MB+ for nailpolish, etc.
I just now used https://start.spring.io/ to generate a project using Spring web, Spring security and Spring data JPA (Hibernate).
Thanks - I haven't used the Spring Generator for several years now. However, I think one also needs to include the drivers, oauth stuff, template libraries, etc to get an accurate represention of "standard Java enterprise size". Gonna play with this offline and see how good it has got.
A real monolithic app dealing with videostreaming that I have been working recently, was based on Spring Boot and AWS SDK and it was a 82 Mb jar file. It had the drivers, oauth stuff, a couple of template engines for business reasons (Handlebars and Thymeleaf), database and queue drivers etc. It could be maintained and extended by a junior developer, because it had established design patterns and they only needed to follow some project conventions. We had multiple releases per week at engineering cost of less than 25k€ per year.
I would not be able to build something like that with that budget on Go.
Most projects won't stay a hello-world REST service, there would be no point of doing them. They will grow and most likely make use of a bunch of CRUD features, on which we have a lots of experience in various languages and frameworks can solve a good chunk of any problem that might come up (AuthN/Z, session management, endpoints, safe parsing from and to json/url/forms, etc).
Spring (besides itself being modular, so you only "pay" for what you use) will solve all of that for me, so I only have to write the small amount of business-relevant code and be on my way. Later on, some other developer who knows spring can join the project and feel ready at home.
Compare it to a buggy, slow to develop, slow to onramp home-grown half-solution, and it's quite a clear tradeoff, unless there are very specific requirements that make the usage of frameworks a no-go.
> You can use the battle-tested libraries wrapped by Spring directly. For OAuth specifically, Spring does very little.
Then you have to work to make the libraries all work together. And deal with updates. Spring Boot allows to to update all libraries together, and know that they work together.
I was talking more abstractly, in that understanding a given feature to be able to configure it properly is not optional (besides asking someone else to handle some part of the complexity e.g. third party authentication services in this case).
Maybe? Depends on the framework. I've been using some Micronaut lately and it's a Spring-inspired framework where a lot of stuff Spring does at runtime is done up front at compile time.
The result is apps start really fast, can be compiled to a standalone native binary with GraalVM, use little memory, and errors that would once have resulted in a complex exception at startup now yield reasonable compiler errors instead (it has compiler plugins to make this work well).
I can't say I've spent much time messing with annotations or config files in this project. Certainly, what little time has been spent on the framework is more than saved by what it does.
> It sounds good but in reality people end up spending time messing around with config files and annotations.
I use Spring Boot at my day job and write mostly web services. I don't spend time messing around with config files and annotations. When I create a service class, I annotate it with @Service, and that is mostly what I need.
Example:
@Service
public record ItemsService(ItemsRepository repo) {
public void doStuff(String country) {
var items = repo.findByCountry(country);
// do stuff with items
}
}
Later versions of Spring Boot has reduced a lot of the annotations necessary, like @Inject if you use constructors etc. There are of course other annotations and configurations, but 90% of what I do is similar to the example I gave above. Things may have changed since last you used it, but the amount of "magic" and annotations is often much less than what is posted in these types of discussions.
Don't know why this is down voted its absolutely true. When our tech leadership reviewed the numbers we changed auth design without touching the application code base really. Saved a ton of work considering we have many microservices.
I found String (Boot) a horror to work with. Annotation based devt with super weird errors. The great thing was that most errors where run into by many before me so could be solved by a simple web search/ Stack Overflow article.
To me being able to CTRL-click my way into the libraries is very important. Overuse of annotations (a.k.a. magic) breaks that. It is what monkey patching is for Ruby. The beginning of the downfall IHMO of an otherwise great language.
I don't see how writing "one more OAuth client" or "one more login thing" using Spring Boot is not reinventing the wheel in itself.
If you really care about "not reinventing the wheel" there are ready-made paid solutions such as Auth0 and Cognito, plus self-hostable open-source options like Keycloak, Authelia, and Dex.
Also, Spring Boot itself uses third-party libraries for OAuth and Authentication, like Ninbus, which people can drop-in in their non-Spring Java apps.
> Spring Boot gives you a set of dependencies that all work together.
But spring boot deps is infamous meme.
> Then you don't have to spend time messing around with things like OAuth and authentication
Yeah. The funny thing is reality is quite complicated and spring supports a lot of (almost) documented cases. But 99% javaspring developers do not care. I met quite a lot of experienced devs and only 2 of them know how to optimize application start or which errors Kafka wrapper would not retry and so on. Half of the non-default situations are solved via reinventing the wheel because of a lack of understanding of nuances. I can't say people are dumb, many of those devs are smart. I tend to say that ultra-framework kills people's expertise and in the long term hardly saves resources.
> I tend to say that ultra-framework kills people's expertise and in the long term hardly saves resources.
You can use as much of Spring or as little as you want. Don't want Hibernate? Use JDBC template.
I have noticed that people who don't use a framework, just end up inventing their own bespoke framework, which unlike Spring, is not documented and has no help available online.
Otherwise intelligent devs assume they can do a better job without all the "complication" and "bloat", but then just end up with homegrown unmaintainable crap that does half of what the frameworks offer for significantly more effort.
> all the "complication" and "bloat", but then just end up with homegrown unmaintainable crap that does half of what the frameworks offer for significantly more effort.
I don't see how you deduct the conclusion of reinventing wheels is the only solution of overcomplex and far from ideal frameworks. But you can categorise this deduction also.
Agree. I have seen same thing where many people maintain a home grown crap called kitchen and think they can do better job than getting ready meal from restaurant.
I tried truffleruby for the adventofcode challenges.
Almost everything ran faster with normal ruby.
I got a stack too deep error in truffleruby that I didn't get in normal ruby.
I don't recall having more problems with memory with one or the other.
There was one case where truffleruby was really useful though.
Thus I'm with you that it's not a systems programming language. It seems good for processes that stay ON, like servers.
But then when compared with PHP, during development you don't need to worry about the server, it just takes the most recent version of your code.
Surely hot-reload can be a solution with java, but in practice it's a complication, especially if you're part of a team/project where there is no hot-reload support.
The JVM doesn't attempt to compile a method until a method has reached 10,000 executions. So it's extremely likely your code ran the entire time in interpreted mode. Even if a method did get flagged as hot, you've then got the time taken to do the actual compilation to native code to contend with.
If you're trying to write systems code and want to use the JVM, GraalVM can do full ahead-of-time compilation and will get you almost instant start up with all methods natively compiled.
Yep, he is comparing the most bloated enterprise framework to lean Go apps. This article is so wrong in confusing Java with their particular monster project. You can definitively AoT-compile in Java, there are modern lightweight frameworks like Javalin, and I think the language itself now allows to write web services without any library.
>Edit: im not advocating writing 'ls' in java, and I would also agree that java uses more memory for small programs, so its not a systems programming language probably.
I dunno, some databases and things like Lucene are written in Java.
"cargo cult" really?
Have you ever built maintained big a Java codebase? Spring is a marvel that sprang out of the experience of very, very talented people.
It can be misused, of course, like any other technology, but it is a huge productivity booster.
yeah, this author reminds me of the guys who think simpler is better because they haven't read the whole documentation on why the thing is complex in first place, do they end up reinventing the wheel badly as they start to grow and discover problems already solved in mature frameworks
the lack of specific mention of scenarios and features beyond dependency injection suggests ignorance IMHO
I find this very common when working with JS backends. Once you get serious, you end up replicating a lot of the complex concepts of frameworks like Spring or .NET
Edit: im not advocating writing 'ls' in java, and I would also agree that java uses more memory for small programs, so its not a systems programming language probably.
Just use new() it's pretty fast.