Oh, I wasn’t aware Node had originally pitched performance. Was it only in the context of single-threaded IO?
Yes, from the hiring perspective that “isomorphism” makes a lot of sense on paper.
Pardon me, in your third paragraph, what “other factors” are you referring to?
As for the companies that made such a move, well, it’s their choice. There’s always a reason, it boils down in the end just to whether it was well justified at the moment. Time will tell.
A reasonable argument in my decision making was Spring’s huge memory footprint in a microservices architecture deployed to public cloud. Go’s profile was nigh of 100m peak RSS for a given payload, while Spring’s was 1.5g, per instance. I could just take one commodity nano VM at $5 a month instead of multiple 1g-4g VMs at $50-150 a month each. FinOps. It’s a common theme, suppose you have a service mesh with many tiny services, it does sound right to have them utilize just as few resources and be spawnable instantaneously on-demand. So a mixture of Java and Go services comes up quite frequently.
But then you need to factor in a lot of other costs and the fact that Go’s GC will hiccup anyway and potential goroutine leaks could bring down the entire cluster. And so on. That’s just to say that—on paper—it might look assertive, while in reality you would get to realize that there’s so much opaque minute detail behind the Spring or Quarkus APIs that once you realize that it’s missing in your Go or Node code base and get to reintroduce it (wasting hours), you end up with something that need not perform just as well. The more stuff you plug into your Go or Node lightweight frameworks, the more integrational complexity is on you and the less agile you get. It’s all their choice.
There are ways to minimize JVM resource utilization. And you don’t need to always use Spring, and you can have instantaneous start-up times with persisted app state. To me, it’s getting harder and harder to justify Go or Node on the backend.