Hacker News new | past | comments | ask | show | jobs | submit login

So with this, the last thing Go had going for it over Java is gone, right?

Java has an obviously better type system, while Java originated the billion dollar mistake, Java also at this point has much better practices around handling nulls than Go, Java's jars are more portable than go's binaries, Java's GC performs better, Java has a more mature ecosystem and more libraries... Java has better IDE support and comparable compile times.

I guess at this point the only major difference is that you can teach a 3 year old to write Go more easily, so ChatGPT produces correct Go more easily than Java, and the dependency management story differs a little (though I don't think you can really call a winner or loser on that one, it's just different)




As a language java's not that bad at this point. But it still has cultural issues.

Every java project I encounter tends to be overengineered, has tons of useless boilerplate code and ends up throwing mile-long stack traces as a result. Also, somehow maven manages to be even more unreliable than npm as a package manager. I still frequently encounter situations which seem to only get resolved by throwing away my .m2 folder.

Furthermore, since the language evolved so much in recent years, it's hard for newcomers to know what the best practices are. There's at least 6 different ways to iterate over map values. Which one should I pick?


> I still frequently encounter situations which seem to only get resolved by throwing away my .m2 folder.

I have my own issues with Maven, but never in my 16 years doing Java have I done that. What the heck leads you to believe doing that may fix something at all? .m2 is just a cache, it has pretty much zero impact on whether your stuff will build unless you had installed things there that are not available on a configured repository or something similar, which would of course be your mistake not the tool's.


> m2 is just a cache, it has pretty much zero impact on whether your stuff will build

Well… the cache can be broken. I’m not a Java dev so have no horse in this race but “it’s just a cache” doesn’t mean “it can’t ever be a problem”


Having worked with maven for about 15 years… it’s largely not a problem anymore. Unless you publish snapshots and depend on them.


If “it’s just a cache,” you should never be able to fix a build problem by nuking your cache. But I frequently can.


Might want to enable strict checksumming on. I think that’s still a thing. Or are you depending on release artifacts that change?


Depending on what is in your cache, Maven will satisfy dependencies differently. You can go out of your way to nail down your dependencies (analogous to a Javascript lock file) but it goes against the grain of how Maven is designed. Maven encourages pretty sloppy dependency resolution by default, and that's what you'll see if you look inside the artifacts you depend on.

There may be other issues as well, because this was an issue that affected Maven throughout my years of using it, in contrast to SBT, which in my experience had this issue much less often (though it definitely did from time to time.)


Can probably happen like it can happen with any dependency management system - if you set wildcard versions (or no version at all?), different systems can have different versions of the dependency cached. Eventually this can lead to surprise build failures.


Completely agree with you.

I stopped working with Java not because the language or the ecosystem. I stopped working with Java because Java developers and their culture of over-engineering everything defending it as "clean code" and "good practice".

But this seems a taboo topic in a culture infested with "good practice gurus".


Theoretically, choosing a JVM language with a good culture seems like a very attractive option. Scala has its own cultural issues, but maybe Kotlin gives you most of the nice parts of Scala without attracting the type astronauts? Maybe Clojure is an option as well, if you're willing to give up static typing? I'm leery of a language with the full power of Lisp macros, but from a distance, the Clojure culture seems to be very pragmatic.


This seems pretty uncontroversial in the Java space.

I think most Java developers will wince at clean code. Clean Code made sense in contrast to the pervading gang-of-four norms that preceded it, but I don't think many people would recommend the style today.


In my experience, there's a non-trivial number of "Java developers" stuck in the Clean Code or Design Pattern All The Things modes of development. They want every project to look like a J2EE demo.

Those types of Java devs can use a lot of interesting sounding terminology but they overengineer everything and commit ludicrous amounts of code that doesn't tackle the problems at hand.


Couldn't agree more, and have found people in disagreement with my stance on that [0]. Although my context is C#, I reckon it's similar.

0: https://news.ycombinator.com/item?id=35486983


https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris... isn't too far removed from some of what I've seen in big tech, especially architecture-wise. Certainly less costly absurdity.


Is your program really complete if you haven't implemented all of the design patterns in the GoF book? How will it even work without an AbstractFooFactoryFactory? How else can you make stack traces unreadable to the point of being useless?


> There's at least 6 different ways to iterate over map values. Which one should I pick?

Didn't stop people from using Javascript.

> I still frequently encounter situations which seem to only get resolved by throwing away my .m2 folder

And I frequently encounter issues which seem to only get resolved by throwing away my node_modules folder (and package-lock.json).

Point being every language has its issues but Java gets knocked down constantly regardless.


If it's snapshots causing issues that clearing .m2 is fixing, try adding `-U` to your mvn execution instead, that forces snapshots to be updated.

Do you run `mvn install` for your local project under development, or any of its locally built dependent modules? Are all of those at a snapshot version? If not, I think you'd need to delete it from .m2 (or clear all of .m2) to get updates to it into .m2, since even for the local cache I don't think maven will override a cached version during install.


Don't use ambiguous dependencies and you won't need to flush your cache to get a newer version.

I would say the culture is not an issue of Java but of popularity. If Go became the shovelware language of choice, you'd see a lot more bad Go.


Gradle is a better build tool from a purely architectural point of view (though it is quite disliked, I believe mostly due to the ultra-complex android build system being built on top and giving it a bad name), but the Maven repo system is absolutely better than most contemporary repositories, proved by its comparably rare side-chain attacks.


Also Groovy kinda sucks


They should just make the right decision and deprecate all Groovy in favor of Kotlin.


It's not like the kotlin DSL comes without problems. Configuration time is most of the time about doubled compared to Groovy, type hinting and completion often end up being slower _and_ worse than Groovy, and if you're lucky, your IDE suddenly can't find a random extension in your classpath and your entire build.gradle.kts or plugin becomes a red, squiggly mess.

Unfortunately, Gradle is the best build tool I've used for complex systems. And it still fucking sucks.


You hit the nail on its head. Deprecate Groovy and allocate all resources to make Kotlin more viable.


I dislike it because of the ultra-complex build system that over-ambitious devops engineers ended up building in my shop. Our build system was the hardest part of our code base to reason about, which is pretty screwed up.


Well, that’s on them — were they developers in any language they could have also made a mess there. I have seen my fair share of random custom maven plugins as well, which have more bugs than features.


Perl programmers have been making that same argument for literally decades.


Pre-java 8 (Java.old) and post-Java.8 (Java++) are philosophically two different languages.

Now, 21 is coming and while the changes aren't quite as in your face, the change in philosophy is just as radical.

Java would be better regarded if less time was spent on "look at this cool new feature" and more spent on "this is how you should use Java now". Or even, "here's how to safely modernise your codebase".

Venkat Subramaniam does some good talks on this but I would prefer if the Java language team were leading it.


    while Java originated the billion dollar mistake
By the "billion dollar mistake", are you referring to null references [1]? But null references were introduced in 1965 in Algol, by Tony Hoare. They long predate Java.

[1]: https://www.infoq.com/presentations/Null-References-The-Bill...


I don't think it's about the existence of null references, but how Java is using them. Null references can be a useful, e.g. Kotlin has converted them to be useful (with nullable types).


Using nulls in Java is mostly a choice on the part of developers; even if you can't migrate to a JVM-targeted language like Scala, you can still adopt practices like null objects [0].

[0] https://en.wikipedia.org/wiki/Null_object_pattern


Speaking of Go and Algol...

http://cowlark.com/2009-11-15-go/


My experience in small companies told me that younger generation didn’t choose Go because Go is objectively better than Java, but because they actively hate Java for it being out of fashion.


I think Java is an ugly language riddled with roundabout ways of implementing modern features, but I can't fathom why anyone would pick Go over Java. It's slightly worse in almost every way other than writing shell scripts or massively parallel workloads from scratch.

In my experience, Java's problem is the same as C++'s when Java began to take over: you usually encounter it in projects stuck at ancient runtime versions, with ancient libraries, full of code nobody dares to touch in case something collapses. The same will happen to Go, and it'll happen to whatever popular language will displace Go as the trendy language to learn; it's just a consequence of keeping around legacy code and not spending the time and money on migrating as soon as possible.


I wrote Java code for 20+ years and I can tell you exactly why I prefer Go: it produces native binaries.

I mean, it’s also less verbose, easier to start a new project, faster to startup, has far fewer configuration knobs, has native dependency management, is far easier to build CI/CD for, compiles more quickly, has very few NPEs gotchas, has value types, and avoids idiomatic boilerplate.

Go is far from perfect, but it has clear advantages over Java and I would not go back.

But the thing I love the most is the lack of a JRE. If I build for a target, it runs there.


> I mean, it’s also less verbose

Are we talking about the same language? Or maybe you consider writing "if err != nil" every few seconds as good exercise for your fingers...

I think we shouldn't point fingers at Java when it comes to Go's verbosity.

I can simply do dict.contains("foo") in Java vs having to go through a verbose hoop:

    if val, ok := dict["foo"]; ok {
        //do something here
    }
Common operations like filtering/transforming collections are very concise in Java - I'm certain doing so in Go will be a bunch of more boilerpate:

    collection.stream().filter(...).map(...).collect(...)
Don't get me started on having to go through iota hoops to declare enums.


Go's language maintainers refusal to add list operations / comprehensions and you having to type it out by hand every time is something I hate everyday while I'm getting paid to write it.


I’m guessing these will come with generics. Wasn’t that also the case with Java?


Go generics already shipped. I wasn't around for Java 4 days so I don't really know when the main operations shipped but one language maintainer was categorically against it so I wouldn't hold high hopes.


Go generics are still in their infancy. Java generics enabled a lot of functionality that came later. But java generics and particularly type erasure are nothing to write home about, and Go maintainers were also said to be against them, so who knows if comprehensions or other features might pop up one day.


Well, I listed a number of things I prefer about Go, and the verbosity I was talking about was the classic ButtonFactoryFactory and other naming classics. It’s true however that I ended up throwing that stuff away from my Java code and started to enjoy life again.

But I can say honestly that I prefer Go’s error handling, which I find tends to result in errors which are actionable. I think it takes a lot more effort up front to get Java exceptions to work rationally.

If I could change Java then the thing I’d do is I’d make it necessary to declare all exceptions in a method, but get rid of caught exceptions. So you can see what’s coming, even if you don’t have to deal with it.

In terms of stream operations, well, I write a lot of typescript lately and just like in Java, I find that I end up having to fall back to regular loops quite often. I’m not convinced that stream operations are useful in as many use cases as people would like. For example, the moment something can throw an exception, things are going to get gnarly.


> If I could change Java then the thing I’d do is I’d make it necessary to declare all exceptions in a method, but get rid of ~~caught~~ checked exceptions

That's pretty much what javadocs contain already


Name one FactoryFactory in the JDK


FactoryFactory is (or was) a well known parody of the multiple levels of abstraction Java developers were encouraged to make when performing even simple tasks. This started with an insistence that we should use getters and setters for field access, and it basically went downhill from there.

This kind of nonsense was endemic to the Java engineering culture while I was working in it.

Perhaps the worst example I saw was during the fluent API craze where someone in my team replaced a constructor call for a Button with five lines of fluent Builder pattern gibberish.

The point of mentioning FactoryFactory was to poke fun at the culture surrounding Java, which ended up being one of the reasons I stopped using it, because idiomatic java stopped making sense to me.

Hopefully it’s changed now.


That was one reason I liked Go as well, it's no longer the case though, you can produce native binaries with GraalVM.


Last time I used GraalVM, there were huge holes in what language features were supported, compiling dynamic code was hit-or-miss, and also its cross-compilation story was not good (requires you to compile it on the target architecture). Perhaps my information is out-of-date now?


Yeah I find the argument for Graal to be very disingenuous. It’s certainly not an out of the box solution; I’d say it’s just another Java technology stack we would need to learn. And there are so many of those.

So I guess I also like Go’s batteries-included standard library.


A lot of people don't know what's best e.g. beginners. A lot of the Go and Node crowd take it own as a mission to fame by stepping on Java. It's worse when a lot of these personalities don't even code except make youtubes videos or blogs.


It is about iteration and interpretation for me. For Java, there is more to consider, there is more to build, there is more to run, and there is more to move around. With Go, things tend to be faster and smaller. It is not feature soup... yet.


Instant compilation, single binary deploys, integrated linter, extensive standard library, and less magical performance. It's way fewer decisions and way less ceremony before you start writing actual code.

And, IMHO, duck typed systems are better than the Java style OOP.


Irrational Fear of Java is one of the most confusing things among startup stage companies.


I see Java/Kotlin as a secret weapon for startups. Too often I read about startups that struggle with immature libraries, smaller eco systems and reinventing basic functionality. Problems that they would not have if they chose a mature technology.

These blog posts mentions a company that has to write their own database library, auth services or other basic functionality. Sometimes they fix so many issues with the core language that they become close partners with the core developers of the programming language. I can't help wonder if the competition also reads the post and just smiles before they go back to actually solving a business problem. To me it's a symptom of choosing the wrong stack, even though working on non-business related problems may be more rewarding to the individual.


I’m reminded of “Fire and Motion:”

> Think of the history of data access strategies to come out of Microsoft. ODBC, RDO, DAO, ADO, OLEDB, now ADO.NET—All New! Are these technological imperatives? The result of an incompetent design group that needs to reinvent data access every goddamn year? (That’s probably it, actually.) But the end result is just cover fire. The competition has no choice but to spend all their time porting and keeping up, time that they can’t spend writing new features.


Sysadmin here.

It's true Java still smells like "corporate" and "slow". And when I say "slow", I don't mean the runtime speed, but the company speed ;)

Until not a long time ago I've been maintaining a bunch of Sun|Oracle|J9 Java 6 + JBoss 4.2.3 + AIX|Linux + PPC|x86_64 ... That was not fun, and the task of moving it to more modern platforms was insurmountable to us. No wonder Azul has support for Java 6 until 2027.

But it's true the platform has changed greatly in every way: The VM, the ecosystem... are so different now. And although as a language, it still feels strange to me (I just use Python), now that I manage a fleet of modern Java and Scala microservices, I don't get scared when I hear the name "Java" :)


Yeah fair. Old JVM land and Modern JVM land really are worlds apart.


It's not. Java programmers I've seen don't use the latest idioms; they use pre-8.0 Enterprise Edition-ware because that's what they've been taught. It's a hideous sight and it has a lot of inertia. That's why I prefer kotlin.


What does this have to do with Java? As in there are no Javascript developers that use Promises instead of async/await?

There's always legacy and people that live with it.

It's a feature that there's no history now?

And soon we're going to cry about Kotlin developers that don't use the latest idioms... oh give it a few years...


It’s all relative, though. One of the biggest complaints people have about the JS ecosystem is the lack of legacy: people do move from Promises to async quickly, when React switches to hooks everyone switches quickly, etc etc. For better or worse you can’t say the same about Java.


> people do move from Promises to async quickly, when React switches to hooks everyone switches quickly, etc etc

On Reddit or HN? Definitely in not the real world. Plenty of projects and even recently created 1s don't always use these new ways of working. Even on Reddit when I see a new project getting posted it doesn't have this trait you mention.

And lots of companies are still on Node 14/16 or even older :( only forced to move by e.g. AWS lambda runtime requirements...

And Node developers are still using ExpressJs and promote it all the time. Guess when Express 4 was released? 5 has been in beta forever with no updates. Yet, most promote it and ignore the other options. Lack of legacy? This thing is 10+ years old. It's barely maintained... what's the difference?


It's not the language that gave me PTSD but the userbase, "best practices" and culture around it.

Java itself is great these days.


I was going to say, Java is fine, it's the architectures and mountains of code spread across loosely coupled architectures that does it for me. I wouldn't mind a modern day Java project, as long as it's free of the 20+ year old dogmatic practices. I'd have to unlearn a lot of those myself, probably.


Also the culture of those that stuck to Java because they didn't have the curiosity to learn anything else...


Well yeah...? That's a big part of why I mostly use Java, so I don't have to constantly figure out basic stuff in whatever language/framework that's hot this week. The language is just a tool. It's not an ends in itself.

I can spend that time building things instead. If I have an idea I can implement it. Downstream dependencies are rock solid, and the language changes at a manageable pace.

Like I did some stuff in python the other week, and every other line I wrote had to stop several minutes and figure out basic syntax stuff. Just a pain in the ass. Like I probably could take the time and freshen up on python and be up to speed in a few weeks, but that's a few weeks I'm not moving toward my goals.


IMO, Java won't be decent until Valhalla is completed. The broken type system is a really big downside compared to C#.


Can you compile so that JRE is bundled with the program?

I'm a C# developer and I also have a kind of resistance of java - having to install JRE and now the minefield Oracle made with the JRE, I'm hoping not only that I don't have to code in it, but that I don't have to use ANY Java program just to avoid the runtime or have to choose between different versions of it.

And then the .jar files and how you execute them in command line is different (java -jar), maybe it is simple, but it is different than a plain executable file.


You can use jlink to create a custom runtime you can distribute with your application, so that your users don't need to download a JRE/JDK. You'll still need to run this with the java command.

You can use also jpackage to create an executable file you can just double-click (.exe on Windows, whatever on mac and linux).


Yes, there is in fact not even a JRE for quite some years now.

Also, Oracle being a minefield is just bullshit - they are the ones that open-sourced the platform completely to the point that their paid version is only marginally different, but OpenJDK is the reference implemented. They are surprisingly good stewards of the language.


Our employer actively tracks runtime usage and makes sure we have no O in it.

> The quantity of licenses is determined by the total number of employees, not the number of employees using the programs https://www.infoworld.com/article/3686611/oracle-per-employe...

> Employee for Java SE Universal Subscription: is defined as (i) all of Your full-time, part-time, temporary employees, and (ii) all of the full-time employees, part-time employees and temporary employees of Your agents, contractors, outsourcers, and consultants that support Your internal business operations. The quantity of the licenses required is determined by the number of Employees and not just the actual number of employees that use the Programs. https://www.oracle.com/us/corporate/pricing/price-lists/java...

Excuse me, but isn't that a minefield?

Another point I don't want to use java: Now I have to understand what is Java SE and if the runtime falls under it or the development tools or how my users use the software, whether we now how to license every user that won't even use that program and even any people that interacts with our business. Pure maddness.


Are you using Photoshop without a license as well or how is that relevant? This is about Oracle’s JDK you specifically have to install and have a paid license to (actually, they also provide a free version if you stay on the latest LTS release at all times), and is meant mostly for governments and such.

Will you uninstal linux because Red Hat has a paid support version as well?


So if i have JDK (Which I must) I do have to buy those licenses.. for all our employees?

> The Java Platform, Standard Edition (Java SE) and Java SE Universal Subscription from Oracle include the Java Development Kit (JDK), and Java Runtime Environment (JRE) https://www.oracle.com/java/technologies/faqs-jsp.html#:~:te....

Do I not need the Java SE subscription to use JRE? Do I not need the Java SE subscription to use JDK?

I have so much questions and because there is even place for questions, I better avoid this thing.


> So if i have JDK (Which I must) I do have to buy those licenses.. for all our employees?

No, use OpenJDK like the rest of the world. It’s free and open-source.


Do you have to buy a license for a regular Linux kernel? No. The exact same is true for OpenJDK. Just download any, for example one that is packages by your distro, or there is sdkman for developers to let you quickly choose from multiple vendors and any version.


Please help me understand.

> To run your Java 8 application, a user needs the Java SE 8 Runtime Environment, which is available from Oracle under the Oracle Technology Network License Agreement for Oracle Java SE, which is free for personal use, development, testing, prototyping and some other important use cases covered in this FAQ

https://www.oracle.com/java/technologies/javase/jre8-readme....

Straight from oracle.com.

How is this free? Of course I don't need to pay for Linux kernel. Of course some products feature paid support. But how can I justify the quoted text that Oracle JRE is free?

I was going to say to the guy that decided there must be no Oracle Runtime at our company (some software doesn't work without it, I have no status if workaround has been found) - Hey, maybe Java SE is just the support/patches stuff and maybe we can use runtime? Until I stumble on that text - free for personal use, etc...


You're confusing Oracles JDK and JRE with OpenJDK and that JRE. Oracle takes the OpenJDK and recompiles it, whitelabels it, and licenses it under their own license. The OpenJDK which is where all the development occurs is true open source.


> Straight from oracle.com.

> How is this free?

Yes, don't go to oracle.com.

This is what you want (it's the same thing, but free open source): https://openjdk.org/


If you want to use the Oracle runtime you need to pay Oracle. But the code itself is open source and you can instead use the Azul, Amazon, Red Hat, BellSoft, etc.. runtimes.


Finally got to the answer. So it IS paid from one particular vendor. I understand there are free options. But that makes it a mine in a field if you are not knowledgable enough. It went this deep into threading to really get an ack that there is a big red O' mine in there.

And there is one piece that wont run without big red O... :(


Virtually no one has trouble with this in practice. You use OpenJDK unless you have “special needs”.


It's the same mine in a field as RHEL (a commercial Linux distribution). However, that has never prevented anyone from using other free distributions.


> But that makes it a mine in a field if you are not knowledgable enough.

That seems like looking for a problem where one does not exist to be honest.

The peer comment regarding RedHat is spot on. Yes you can purchase a Linux distro from RedHat and pay lots of money.

That doesn't mean anyone will argue with a straight face that you can't run Linux for free!

It's the exact same scenario with Java. You could pay Oracle for a Oracle JDK if for some reason you reall want to, but approximately nobody does that.


Since OracleJDK is also just OpenJDK with a logo, I don’t believe that there is any software that runs on any recent OracleJDK but not on OpenJDK.


It may be windows specific thing like registry looking at specific runtime thing or whatever... This software doesn't run with OpenJDK: https://support.ismacontrolli.com/Software%20%26%20Tools/iSM...


The vast majority of Java is open source (Open JDK). You can get builds from Amazon (coretto), Microsoft, Red Hat, SAP, ... that do not stupid licensing requirements of Oracle.


> they are the ones that open-sourced the platform completely

While I agree with the bullshit claim, Sun was the company to open source Java. Which itself goes back to IBM blocking the community process in order to force an Apache licensed Java implementation, Sun releasing the OpenJDK made most people happy without killing its embedded cash cow.


completely is the important word — there were plenty of paid-only tools back then that were part of OracleJDK but not OpenJDK. Oracle made them open-source.


Yeah I think Oracle doesn't get the credit they deserve for OSS-ing stuff like the Java Flight Recorder.

Of course their continued work on the big JVM features and GraalVM are huge for the community too.


This is just the public perception. Being a full time Qt dev feels more and more like using Oracle software, where the software itself is FOSS and free to use, but the company behind it makes it look like it is not, with all the negative press.


If you wish to avoid JRE, you can use Graal VM and compile to native AOT executables. Just takes a few minutes to download and play-around. (Even timed it with another Java disbeliever here on HN)

https://www.graalvm.org/latest/reference-manual/native-image...


You can’t use anything reflection-based I think.


You can, you just have to have a config file that lists files that might get reflected upon.

This is available for some of the more common libraries (there is definitely more work to do here), and you can also use an agent, run your code on a regular JVM and it will collect the runtime accesses and create that config file for you.


When I tried it the file size ended up way bigger than just packaging a JVM would be


That isn't a general behaviour. You can get <5mb binary size for modest apps. Likely have one or two "bad" actors contributing to binary size. Try the Graal VM dashboard to identify which modules are bloating up the binary.

"Use GraalVM Dashboard to Optimize the Size of a Native Executable"

https://www.graalvm.org/latest/reference-manual/native-image...

Tutorial that shows reducing binary size from 17MB to 862KB!

https://docs.oracle.com/en/graalvm/enterprise/22/docs/refere...


Single executable is useful for end-user. It is not very relevant on servers or for development.

On the other hand portability and debugging experience with jars are vastly better. Just consider a developer on Mac with Apple silicon cannot use the same executable as on Amd/Intel server, while jars are cpu-independent.


I think you can with 3rd party tools, as with Python, but why would you? If your app is literally a single executable with no additional resources, maybe it makes sense, but otherwise, just bundle the JVM like jetbrains does.

And as you mostly use Java on the backend, you're probably running Linux where a free and open jre is packaged, so just target that and not worry about it ever.


> And as you mostly use Java on the backend, you're probably

running in a container.


Assuming you have separate java services, do you really want to bundle a different "minimalistic jvm" on every container, or use containers that share the same base layers?

If you use 200x images based on, say, `FROM docker.io/library/eclipse-temurin:17-jdk`, you only use 230MB + all other JAR/overlay layer sizes.


Yeah. Java isn't particularly sexy. But it gets the job done. I'd even settle for Java 7 still, it was more than enough to get the job done, especially when you are a startup trying to get an MVP out the door that can still be refactored and maintained somewhat easily when you bring in more skilled developers.


You can tell when a programming language conversation has become toxic when negative qualities are assigned to the people who choose (or apparently don't choose) someone's favorite language.

In this case, it's an "irrational fear" of Java to have preferences beyond Java.


It's not just startups where this happens. In older companies you have older "leaders" that claim to have been hit by Java 5-10 years ago so don't trust it. Yet they've never tried it again since.


> Irrational Fear of Java is one of the most confusing things among startup stage companies.

This so much. It's exhausting to experience startups shoooting themselves in the foot by not using Java where it's best simply because it's not hip.

Golden rule for startups, spend your innovation capital on your product not on trendy (unproven, immatue) implementation technologies. Use boring (aka mature, high performance, best tooling) technology.


Startup stage companies work on smaller codebases and primarily need to go as fast as possible from nothing to a working prototype without the need to use many advanced features.... Java is a good language, but the community around it, whether it's experienced developers or libraries are built for enterprises, who work on huge codebases and their primary concerns are security, performance, scaling, testing, all of which are secondary in most startup environments and usually only start to matter years after the startup codebase is created...


> security, performance, scaling, testing, all of which are secondary in most startup environments

I've done quite a few startups by now and I can say that in all of them the initial quick-n-dirty MVP codebase lasted well into the era where things like "security, performance, scaling, testing" became a priority.

Unless you are absolutely committed to throwing away the MVP (and I don't see how you could because as soon as sales team sees it they start selling it and you won't have time to throw it away), your best early move is to use technologies that will take you to the long haul because you will be stuck with that initial code for a very long time.

(This doesn't mean architecting the whole system for scale you won't hit in years, mind you. Just to use technologies that will make the transition to a mature engineering team easier. Such as Java, given the topic of this conversation.)


In my experience the zero to something stage lasts way longer than people think and the cost of using immature and badly suited stacks like Typescript/etc bites way sooner than people think. They just keep pushing through it and telling themselves this is faster and everyone else is doing it so it can't be wrong.

Case and point I built a new service recently in Kotlin on JVM. It's a SQL translation gateway that maps an internal representation of simplified relational query model onto other databases so you can connect your own MySQL, PostgreSQL, Oracle, SQL Server, etc. Because Java has such a deep ecosystem in this area I was able to leverage JDBC and jOOQ to deliver support for all the require database types in only a few days of work with very clean code and full test coverage that tests compatibility with all the target databases.

Imagine trying to do that in Typescript. You don't have a standardised interface to database drivers so you would need to first choose that abstraction and implement it yourself + adapters for each driver you need to support (i.e re-inventing JDBC). Then you need to handle the different SQL dialects as they all have different quoting rules, different LIMIT/OFFSET syntax, different supported LIKE/ILIKE/etc, there is no library for handling this in Typescript, especially not with support for commercial DBs like Oracle, SQL Server and -definitely- not for more esoteric stuff like HANA and Informix, etc. Just writing a dialect agnostic query builder abstraction is weeks of work, actually handling all the dialects and emulating anything that isn't native for your targets is much longer.

So you might say, "but that is just libraries"... however that -is- the entire point. When you buy JVM you are buying the ecosystem of libraries primarily. The very nice runtime/GC/languages is really just icing. Same way that when you buy Python you are really buying numpy/pandas/PyTorch/etc, the language itself isn't the important part.

Or you could say this is just a problem uniquely suited for JVM and you wouldn't be wrong but isn't that also the point? Use the right tool for the job. JVM is to business logic and solving business problems what Python is to data science and machine learning.

The big epiphany I had recently was that when I build on JVM I don't write much code. I make a design, assemble some libraries in the correct way, iterate on it by refactoring until the abstraction is clean and then turning it on. There is very little mechanical coding involved because everything fits together very cleanly with only business logic being surfaced in actual "code". I feel more like an architect, less like a code monkey and I can ship results way faster because I don't have to work on code that doesn't directly solve the problem. i.e there are much less yaks to shave.


Fun fact: JDBC was one of the (if not the most important) blocking APIs that people tried to convert or redesign / reimplement as async, and failed miserably.

Now, with the advent of virtual threads this should no longer be a problem! JDBC can block all day long, and your app will still scale without a problem.


Yeah will be good to not need R2DBC or the Rx APIs etc.


Most business applications today have a web UI, using nodejs/JS/TS simplifies things by having to deal only with one language and ecosystem. Most business apps today stick to only one database and maybe an additional nosql database. What you did is very unusual for most apps and is something only huge companies would do, like SAP or big banks.


Irrational fear of Oracle is actually a perfectly valid factor in my opinion.


> Irrational fear of Oracle is actually a perfectly valid factor in my opinion.

Fear of Oracle is rational though, unlike fear of Java.


I am not sure there is fear against Java as much as there are better alternatives for many use cases.


I would argue the “many”, especially when we talk about regular old CRUD apps most startups are.


Not fear. Disgust.


that's a ridiculous attitude.


Java has the lingering shadow of Oracle hanging over it.

That legalistic hydra is enough to make you second guess using it.


> Java has the lingering shadow of Oracle hanging over it.

This myth needs to be dispelled.

OpenJDK is Java, and is open source under GPL.

https://en.wikipedia.org/wiki/OpenJDK

https://github.com/openjdk/jdk/blob/master/LICENSE


Would you ditch Linux, because Canonical has been making concerning decisions with Ubuntu ?


False equivalence.

Canonical doesn't own the Linux trademark, nor have the litigation history.


Irrational Fear is actually what is waged on the Java front. People doing mental gymnastics to avoid touching anything non-Java. Cargo-culting. I've witnessed Java devs proposing changing an existing Go project's build system to maven because "that's what they know well". Straight insanity.

People that avoid Java don't do it because of some stigma, but because they have been burned before. Remember that a typical Java project is not the latest version and uses a mix of dependencies that make using newer features impossible. Then, there is no point in updating, you might as well rewrite it. That is the reality.


I work in a Go shop and we try to get rid of non-Go parts of our stack to make things simpler and more consistent.


There are legitimate reasons to chose Go over Java, starting with the build system and the amount of effort needed to produce a single binary. I use Java for 20 years and Go for ~2. I am not actively hating Java but I value my time, specially that was wasted on maven + co.


Maven isn't Java.

I've used java for over 20 years and managed to almost completely avoid it.

As with npm - I think automatic dependency management at the library level pulls in far too much of the world - most of which your code doesn't actually depend on because the one function you need in lib A, doesn't actually require lib B, and therefore lib B dependencies C&D etc etc etc.

Madness.

If you do dependency management at the source level ( using the compiler.... ) then life is generally much simpler ( reflection being the only aspect you need to manage ).


Could you elaborate on what you mean by "dependency management at the source level"? Do you mean manual JAR inclusions into the project?


The key thing to remember is the transitive nightmare of dependencies that you let Maven manage is largely created by the way Maven works!

So if you don't do it that way, just you need to manage the dependencies yourself, but they are much much simplier!

In a bit more detail - at the top of your source code is:

import someorg.somepackge.class;

If you have the source code for someorg.somepackage in your sourcepath/classpath ( and the source of any transitive dependencies ) then the compiler will magically find all the transitive dependencies for class at the class level at compile time. [1]

This results in the minimal number of classes you have to ship and as a result the minimal number of transitive dependencies.

Now your build tool/script ( whatever tool you use ) will need to bring in those dependencies from a versions repo somewhere - but that can be your own source code repo ( vendoring I think it's called ).

Yes - you need need to keep that build config yourself - but frankly that's time well spent as it results in you have proper control over your dependencies, and they don't balloon out of control.

Occasions like some random third party has added dependency D to C which is brought in via an A->B->C chain and then you have some library version clash are much much less as a result.

That's not to say I don't occasionally use jar files in the classpath - but that's typically only if that library is self contained, single focus and small - and again you can put that in your build script.

In my view, Maven magic is part of the problem, not a solution - and to sum up why - it hides the cost of the dependencies - if people had to manually add the chain of true dependencies when they decided to use a apache utility class, they might think twice about whether that chain of dependencies is well designed or not.

[1] With the reflection proviso I mentioned above.


> Maven isn't Java.

The wheels aren't the car. Sure, try to drive around without wheels.


In my experience using Maven is like driving around with a set of powerful magnets tied to the back with string.

You gradually pull in more and more c*p.


I've used Java since it came out and I have never used Maven


"..and the amount of effort needed to produce a single binary."

A couple of minutes ? Even timed this with another HN Java disbeliever who said its too much effort.


I understand that you mastered producing a single binary with your favorite build tool. In my experience if a language does not have an official way to build projects it does end well. Go, Rust and Zig are the prime example that a language should not be separated from its build system.

How to produce a single binary:

    go build


    cargo build


    zig build


  <project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.4.1</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <artifactSet>
                <excludes>
                  <exclude>classworlds:classworlds</exclude>
                  <exclude>junit:junit</exclude>
                  <exclude>jmock:*</exclude>
                  <exclude>*:xml-apis</exclude>
                  <exclude>org.apache.maven:lib:tests</exclude>
                  <exclude>log4j:log4j:jar:</exclude>
                </excludes>
              </artifactSet>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
  </project>

Can you spot the difference?


I would like to point out that the language compiler in Rust (rustc) is certainly separated from its modular build system (cargo).

Also, how so wonderfully and utterly balanced of you to give an old-school Maven pom.xml file with the maven-shade-plugin and exclusion lists, but omit the Cargo.toml and go.mod files.

Also, its as simple in Java as:

Gradlefile:

  apply plugin: 'java'
command:

  gradle build 
Can you spot the difference ?

EDIT: Ok you were asking about creating single all-in-one fatjar ? Add the below to your Gradlefile:

Gradlefile:

   plugins {
     id 'com.github.johnrengelman.shadow' version '8.1.1'
   }
   apply plugin: 'com.github.johnrengelman.shadow'
command:

   gradle shadowJar


> utterly balanced of you to give an old-school Maven pom.xml Sorry I started to use Java a long time ago, not sure what is the "recommended" way of doing things today.

I guess I just need to know which random Github project is the new hotness today that does something that the other languages do out of the box. Thanks for supporting my point of view.

Again, to produce a single binary you need to know/care much less with Zig, Go and Rust than with Java.


Yes, one is a command line command, the other is a part of a build description file, so false equivalents.


Is this a joke? Or you didn't really get the simple point that build description file is not mandatory or even required for other mentioned languages.


But dependencies are somehow needed for Java (otherwise, what are you excluding? Log4j is not there by default)? How is that a fair comparison?


The younger generation chose Go because they're dumb and fell for the Google marketing hype. Most of them have never even used Java outside of school.


The younger generations of coders are indeed more susceptible to hype, but if you go around dismissing things that are hyped, you're going to miss out on a lot of good things. I've got 15+ years of Java experience, and I think Go is a nice language and has some real strengths. From what I can see, the primary difference with Java isn't the build system or the binaries, it's the decision to strongly favor simplicity and readability over expressiveness. The payoff for this is that you can jump into any Go codebase and figure out what's going on fairly quickly. You don't need to pull down the source, fire up an IDE and start drawing object diagrams, you can figure out what it's doing by skimming the source on Github. I find this very useful.


I'm dismissing it because it's dumb, not because it's hyped. Rust also has a lot of hype and is a much better language. You don't have to like Clojure or Algol or APL or Haskell but these languages at least have some intellectual merit. Golang on the other hand... I'm at a loss for words for how deliberately bad it is.


Intellectual merit turns out not to have much merit when it comes to software ecosystems. "Worse is better" remains in effect.


Or maybe they had to use spring / hibernate and ran away.


I’m older generation, choose Go because it’s almost universally better for my use cases.

But what would I know? I only worked in Java for 20+ years…


This is really some shameful discourse. It's somehow worse than the usual fare of "nobody but beginners/idiots would choose Javascript if they had other options."


What are those use cases?


If anything, Google doesnt promote it at all, never had official Android support. They love Dart and now they are building Carbon.


Google is a patch work family where the siblings constantly fight each other for their parents' favor. All the teams are competing for promotions and money that you get through "impact". Go and Dart are just like all the other Google products that get released with much fanfare and then quickly forgotten once their creator's no longer need them for their next brag sheet.


It's also older generation, those who worked with Java in 00s and 10s and got experienced with Java.


I'd rather think they hate it for being a word salad with lots and lots of boilerplate. Go has its issues, but that isn't one of them.


Have you seen the two languages, like.. ever? I’m sorry for the harsh language, but Go is literally much much more verbose than java, and not just recent Java that has since improved, even Java 8.


Citation needed; first off, verbosity is not the problem, verbosity is NEVER the problem; any significant codebase will end up with millions of LOC (and tokens) regardless of language choice.

Second, Java's infamous for having VeryLongClassNameFactoryBeans on the one hand, and deep class hierarchies on the other, with new classes being declared for everything.

Third, it depends. You can set up a HTTP server in two or three lines in Go without 3rd party dependencies. This [1] is the shortest Java versions I've found, and it contains a lot more magic.

I'm biased in favor of Go, but verbosity is not the issue there.

[1] https://stackoverflow.com/questions/3732109/simple-http-serv...


To be clear, you think this (using the web server built into the JDK):

  HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
  server.createContext("/test", http -> http.sendResponseHeaders(200, -1));
  server.start();
Contains too much magic?


No, but nobody's going to use it. They're gonna Spring Boot all the things anyway, no matter how small the task, because "you might need it in the future". CRUD API with 4 endpoints? Spring Boot! Logging server? Spring Boot! That's where the magic is.


Whereas the brave Go developer is going to rewrite a half baked implementation of Spring Boot for every service that ends up growing a little bit more than expected, all on the default go server. Weeeeeeee


"Second, Java's infamous for having VeryLongClassNameFactoryBeans on the one hand, and deep class hierarchies on the other, with new classes being declared for everything."

I have always wondered why Java is hammered for this while Apple is celebrated for this.

CMMetadataFormatDescriptionCreateWithMetadataFormatDescriptionAndMetadataSpecifications(allocator:sourceDescription:metadataSpecifications:formatDescriptionOut:)


I almost didn't believe you there!

https://developer.apple.com/documentation/coremedia/1489454-...

"Creates a metadata format description by extending an existing description with the values you specify."

The description doesn't sound so bad...


> I have always wondered why Java is hammered for this while Apple is celebrated for this.

Indeed.

And it's good to remember that there is nothing in the Java language forcing one to name things verbosely, that's on the developer's personal choice. Just like on every other language.


We were talking about PL features, and now you bring up a style of writing said language (which by the way comes from very different times and is no longer the norm)?

Composition over inheritance has been a mantra in Java circles for a very long time now. The important difference is for example that Go can’t have proper error handling no matter how good the developer is, due to the language not having a proper abstraction over that.


That's a feature not a bug. Go intentionally forgoes abstractions of that sort. I personally prefer well written Java exception handling, but often Java exception handling code is a bolted on afterthought. Go's approach is to make error handling something you have to think about for virtually every line of code, rather than a superstructure built around the code. This can feel slow and painful to write, but it guides the developer toward considering each error case individually, potentially resulting in more nuanced responses to errors. It also makes reading the code dead simple.


> Go's approach is to make error handling something you have to think about for virtually every line of code

But that doesn’t work, and you can’t handle every error at the place of its origin.


Seems to me like it does work, based on many successful projects using it. There's nothing that forces you to handle the error at the place of origin, you can pass it on like you do with exceptions. You just can't bubble it up multiple levels.


Looks like four lines of main() code, the rest is a specific hadler that would be needed regardless of language.

What part do you find magical?


Well technically, you could do async and green threads / co-routines / promises / futures/ etc. (all the same thing technically, it's all callbacks under the hood) on the JVM for many years. Frameworks like Spring, vert.x, and others have supported this for ages. The loom project just makes it a bit easier to use this and provides some JVM level support and optimization for this. With other JVM languages (Scala, Kotlin, Clojure, etc.) this was already quite easy so it's less dramatic if you were already using those languages. My kotlin co-routines will be using loom threads under the hood pretty soon but it's not going to massively change how I use them or what I do with them. Or make a huge difference in performance. My code isn't CPU bottle necked, typically. Like the vast majority of server software (which is IO or memory bottle necked typically).

Go of course has simplicity as its main advantage. That doesn't go away of course. But it's a double edged sword and it can be a bit overly verbose / limited for some stuff.

Maybe more interesting is the trend towards native compilation in the Java world. Graal is a pretty big deal. And with languages like Kotlin, there is also kotlin native and wasm as a relatively new option. That's the other advantage Go has had that is slowly going becoming less relevant: fast start up times and a simpler run-time (i.e. a statically compiled binary that you start).

ChatGPT is fluent in many languages. I've been generating some usable Kotlin code with it, for example. I'm sure it does Java just fine as well.


I would say that Go's error handling is a billion dollar mistake as well.

The JVM is very impressive and a great thing to build upon. The Java standard library is vast and the developers actually care about it (unlike the Python folks, who gave up on having a sane HTTP client library built-in and instead defer to the third-party requests library).

The Java language is not as great, lacking some quality-of-life features (properties and operator overloading, for example). It often suffers from design-by-committee (e.g. https://openjdk.org/jeps/430 ).

And then you get to the web frameworks. The most popular one is Spring, and it's a pain, with abuse of reflection and magic, bad docs, and other issues.


> The most popular one is Spring, and it's a pain, with abuse of reflection and magic, bad docs, and other issues

How is it any different from something like RoR or Django, both of which are well-liked?


Depends if they are talking about Spring the framework, or Spring Boot, the "conventions bootstrapper"

Both of them do not have bad docs, maybe some sub project might have, but compared to RoR (sorry never used Django), Spring's projects docs are magnificent. Their problem could be navigation for someone new to it, or in the case of Spring Framework, just too much concepts.

So, Spring Framework is basically "make everything configurable extensible etc", its code if full of old java patterns for generic stuff (the infamous "AbstractSingletonProxyBeanFactory" , ye, not great), to not only have a very feature full dependency injection, but to allow stuff like AOP and setting up via xml and many other things. This is the base of spring so you will have to kinda deal with its concepts if you hit an issue.

Ruby on Rails was a reaction to things like Spring, it's philosophy is "convention over configuration" in contrast to Spring's unparalleled configuration options. And so Spring Boot is born as a reaction to Rails.

Spring Boot is at its core 2 things

* an annotation engine for Spring configuration, instead of using all those hellish factories or XML

* Sane Defaults for Spring Framework There were also many other projects under its umbrella to streamline with those 2 things other components of the Spring ecosystems, from Http Apis to Repository patterns

Ok so reflection and magic:

* Both are full of reflection. That's how it keeps being very generic at its core.

* Magic is an issue of Boot, you just put some annotation and it would do who knows what. Still way less magic than Rails, as at least you see the annotations, and search for it in the docs. Framework isn't really "magic", as you would have to explicitly configure everything in either XML or through its classes. ( Spring Data does have weird magic, worse that Rails, generating full SQL queries from method names? C'mon)

Nowadays, modern java (server side) either uses Spring Boot (due to easily supporting most kind of infra you might use from persistence to messaging), something specific for their use case (quarkus makes it easy for small image and quick startup) or not use a DI framework at all, as most servers nowadays are fine, and language has plenty of features to not need some management of Injected. I actually see no value now to "minimal DI frameworks", because manually wiring is not actually hard except when you have a circular dependency, which you should not have anyway. (The codebases I had better time working with were either on Boot or had no managed DI)

Spring Boot and Framework new releases actually require Java17, with the objective of being able to start to clean up their codebase from old patterns that were kinda required in the old times.


> Both are full of reflection. That's how it keeps being very generic at its core.

Reflection is not a feature, but a kludge - to conceal how non-expressive the core language is. Add annotations to that, and you might start asking yourself, what value does exactly Java's static type system add here, compared to a dynamic language (say Javascript)? Most of your errors will remain undiscovered until runtime, so why bother?

Things are changing for the better with Java, of course, with the speed of a glacier, but I don't think we'll ever be able to get rid of Spring proxies, or annotations or reflection.


Unless your language is Coq or Idris, its type system will still be way way less expressive than what reflection can do — java’s type system is quite good, it only misses HKTs, and even where static types are insufficient, it has proper runtime types and error handling, so runtime exploration of dynamic data in a safe way is absolutely possible.


New features makes lot of reflection use cases unnecessary.

Spring already bumped base to java 17, and the plan is to improve the internals, but changing public APIs is a different story.

Still, other options appear everyday so you don't have to use Spring. But yeah, glaciar speed, but with that comes low churning rate and old stuff needing very low maintenance.


I have my complaints about Spring, but bad docs is definitely not among them. What do you consider a good docs?


Django's docs are great. For Spring, Google tends to bring up random sites with dubious quality content.


You know there’s official Spring docs, right? You’re complaining that Googling for something shows links to random blogs and Stackoverflow questions. That’s not exactly a phenomenon unique to Spring.


Spring docs are fantastic, take for example https://docs.spring.io/spring-boot/docs/3.0.x/reference/html...


when it comes to full stack frameworks, Micronaut(https://micronaut.io/) is actually good and pleasant to work with.


> I would say that Go's error handling is a billion dollar mistake as well.

I think you'll need to substantiate that one with some solid evidence. I don't see anything wrong with Go's error handling that cannot be explained by developer choice.


Having your business code riddled with error handling code is just bad design, and will inevitably result in not properly handling certain cases simply because the developer would prefer to write the actual business logic and can’t always stop doing that.

Exceptions (especially checked ones) allow for as fine or crude-grade error handling as needed. Don’t get me wrong, Java’s checked exceptions are not without problems, but compared to Go almost everything is better in this regard.


I've encountered several dangerous incidents of mistakenly ignored errors at an employer that heavily used golang due to the language's bad design.


The Manning Book: "100 Go Mistakes and how to avoid them" gives solid examples of common gotchas with Go's error handling.

I have seen the below occur again and again in Go. (no compiler help, so this can only be caught in reviews and not if the reviewer is tired).

#50: Checking an error type inaccurately

#51: Checking an error value inaccurately

#54: Not handling defer errors


Something OR Error should naturally be expressed as a sum-type, not a tuple (product-type).


The linked article has zero references to golang, so it's unclear if the java community has other golang features they're planning on buying into.

This change improves Java/JVM capabilities and that's great. Java is a great language and it's nice that it is shaking off the stagnation/perception of stagnation it has acquired over the years. Golang also has really great capabilities and use cases. Having written tons of code in dozens of languages it seems to be folly to expect one language to rule them all. The features that you don't have are almost as important as the features you do have.

If Java is looking for additional golang cherrypicks I would love to see their start times improve (shortlived java processes are painful, Graal doesn't cover all uses). FFI even in Panama is much more boilerplate then CGO. Go's deployment story is so much cleaner and straightforward then Java. The amount of engineering hours saved through language/toolchain standardized formatting is just monumental. Those are just 3 things at random, their are a bunch of other great ideas to steal.

I also wouldn't throw any shade at Golang's vibrant library ecosystem. There are just so many great active projects written in pure go that make spinning up new services a joy.


golang doesn't really have other features that Java can take from, since Java is so rich, other than perhaps embedding. But it's been the case that the Java development process has been doing an excellent job at taking a better approach than what exists in adhoc developed languages, so it wouldn't surprise me that they would come up with a superior alternative.


Java's GC has better throughput, because Go's GC specifically optimises for latency. This may or may not be the right tradeoff for you specifically, but it is a perfectly reasonable tradeoff.

Jars are more portable at the cost of requiring a JVM installed on the target, whereas Go's statically linked binaries and great cross-compilation make portability mostly moot for server applications.

Also, with Java 21, the JVM brings runtime support for virtual threads, at the VM and standard library level, but there is no real language support, whereas Go has actual support with syntactic sugar around goroutines, channels, and select statements.

Tooling and the sheer number of libraries are definitely Java's biggest draws, but it's not enough to justify choosing Java over Go across the board.


JVM has many different GC implementations, some of them are optimized for low latency, and, in fact, come with guarantees.


If you wish statically linked binaries and cross-compilation, you can use GraalVM https://www.graalvm.org/22.0/reference-manual/native-image/S....

Fully accept that Go has the superior cross-compilation story, though you can do this via Github actions if you really want this for Java. https://github.com/marketplace/actions/github-action-for-gra...

As for GC - Java is superior in nearly all respects. But you are right that common programming libs+paradigms in Java produce too much garbage. This has changed with lean and memory-sensitive frameworks in Java nowadays - like Quarkus https://quarkus.io/


Java also has a low-lat GC which is also better than Go’s at the relevant metrics (ZGC).


> So with this, the last thing Go had going for it over Java is gone, right?

Go still has composition over inheritance, which is vastly more flexible. Go favors explicit (verbose copy-paste, redundancy, use stdlib first, libraries second) over implicit (magic annotations, frameworks everywhere) and I like when I can understand what's happening without holding 10 files in my brain context. Go's memory usage doesn't trigger the OOM killer every 10 minutes. Go favors copy-pasting because what you copy is short and understandable, and function names don't have 10 words in it.

It's always going to be a personal choice, and even though virtual threads bring java closer to where go is, it's still not there for me.


How does a language favor copy/pasting? Do you mean it makes code reuse harder?


It's not so much part of the language but it's an idiom that is said by one of its authors (https://youtube.com/watch?v=PAAkCSZUG1c&t=9m28s). "Code reuse" always has some non-compressible context: another import with a line in go.mod, another repo and dependency, which doc is going to be on another page/site, etc... It makes sense for big dependencies but not for small ones; the threshold where "small" becomes "big" being of course subjective.


A language's type system can disallow certain constructs that you might want to reuse. Mappable, for instance.


> How does a language favor copy/pasting?

It favors as a culture. A little copying is better than a new dependency

Just like Java has culture to setup a single http endpoint one typically adds 50-60 little jar files of Spring Boot starter.


> Just like Java has culture to setup a single http endpoint one typically adds 50-60 little jar files of Spring Boot starter

No, you add a single annotation..


As someone who recently developed in Java (for developing a Jenkins plugin) after not having done so for a long time (~15 years), I feel that Java nowadays is technologically quite interesting with many interesting libraries and tooling. IDE code navigation and debugging support is excellent.

At the same time, the ecosystem can feel messy and overwhelming. There are multiple @Nonnull annotation libraries and it's not clear what the difference is. There are many different testing libraries (some TDD, some BDD) but it seems everybody picks a different one. There are multiple advanced concurrency libraries and concepts, but again not much consensus and everybody uses a different one. Lots of deprecations all over the place.

It's even worse in the Jenkins-specific ecosystem where nearly every plugin uses some deprecated aspect of either Java or Jenkins, and hunting for what the "right" way is supposed to be is a bit of a daunting task.

There are like 20 different JDKs, not sure why.

On the other hand, startup time and memory usage still seems to be a big issue. Starting Jenkins takes forever even with JVM in client mode and with bytecode verification disabled. And when started it uses a huge amount of memory. To date I've never seen one non-hello-world Java app that isn't like that.


Hi there ;) Knowing you, I'm sure you've got it all figured out by now but for any others who are reading:

- Nonnull annotations: you can pick any. Tooling doesn't care and tends to just accept any annotation with a name like @Nullable or @Nonnull regardless of namespace. There was an attempt to standardize this years ago which failed for reasons that are hard to understand from the outside, something to do with lack of agreement on the exact semantics and use cases. No, me neither. Or just write in Kotlin where it's all integrated with the type system and the compiler understands / hides from you the Java annotation mess.

- Testing libraries: it's been quite a long time since I encountered anything except JUnit 4 or 5, but they interoperate so that's no big deal. JUnit 5 is great and very standard so you can't go wrong by picking it.

- Concurrency libraries/concepts: yes this is a problem but it's one found in other ecosystems, and it's one the new virtual threads are designed to solve. The hope is (and I guess we'll see) that everyone can forget about coroutines and reactive programming now, at least where performance matters, and go back to writing old fashioned ordinary threaded code. I guess they'll stick around in Jetpack Compose GUI programming.

- Lots of different JDKs. That is indeed new and unfortunate. They don't actually vary much, mostly in how long major versions remain supported. Amazon is a sort of default choice unless you're doing GUI work, in which case JetBrains Runtime has lots of desktop specific patches.

- Startup time/memory usage. There are simple things that can be done to improve this (see AppCDS). Some of it is cultural however, the people who write CI servers probably don't care about startup time. IntelliJ for example starts pretty fast in the latest versions, because they care to optimize it. For memory usage, be aware that the JVM will default to using most of your free system RAM even if it doesn't need to. It figures hey, the RAM is free, so why waste CPU time and energy on garbage collecting if I don't have to. If you planned to use the RAM for something else, or conclude that this is the "natural" level of RAM usage, it can be annoying however. You can give it a limit or in recent versions set a flag that'll cause it to regularly run GC when the app is idle, to give back memory to the OS.


As for the JDKs, they are basically all just OpenJDK repackaged. Think of them more like debian’s chromium vs arch’s packaged chromium.

You can generally use any.


No they are not.

Azul, PTC, Aicas, microEJ, OpenJ9 are their own thing. And then there is ART.

Also special stuff like Ricoh, Xerox, Gemalto, Cisco among many others using Java subsets to customize their hardware.


Sure, if we are being pedantic you are right. I meant it more like the “majority of well-known” or those with “bigger userbases”.

But the “niche” JVM category is really (perhaps surprisingly) huge.


Because Java is an industry standard, just like C, C++, Ada, JavaScript,... so many vendors want to provide their own take on JIT, AOT, GC implemetations, specially for niche areas like hard real time embedded deployments, where no other GC based language competes with Java, at least at the same deployment scale.


C# already had all the advantages of Go (more or less) and more, yet Go is still growing. This has nothing to do with the technical capabilities of the languages.


To be fair, .NET only recently got AOT compilation and single binary (or self-contained) deployments as officially supported features.


If you target the classic .NET Framework on Windows, you could just ship a single folder for a long time The .NET Framework is shipped with Windows (though not necessarily at the latest version), and the deployment strategy for your average desktop/console app is just "copy the entire bin folder".


That's true. Single file deployments were also supported in .NET Framework, only a bit obscure by registering a callback for the assembly loader and explicitly bundling every dependency as a resource in the build process.


C# doesn’t really have an equivalent for Go’s channels syntax though.

Also, Go doesn’t have inheritance, but does have interface forwarding and structural (as opposed to nominative) type-system - those both very significant factors that influence final program design.


.NET has channels as part of the standard library.

In C# you must write explicit async/await keywords whereas in Go it is implicit, but the code reads almost the same.

I would argue against using implementation inheritance generally and interfaces sparingly in C# anyways.


Being not super familiar with Go, I think C#'s async/await is similar, isn't it? If you want more complex operations, you might want to look into System.IO.Pipelines.


Nah, totally different than channels. And Goroutines are proper managed threads with their own stacks.

As an aside pipelines are terriblely unergonomic. The public APIs are not fully developed and something simple, like IDK creating an actual processing pipeline, is funky as all hell. Creating a pipe wrapper feels dirty.

The buffer management is cool though and sequence seems like it should have just been made a first class slice type..


.NET’s Pipeline type is not intended for processing-pipelines (surely that would be Windows Workflow Foundation and SSIS?) - it’s meant to be an alternative (and performance-optimised) API for reading and writing to IO streams without faffing around with the differences between MemoryStream, FileStream, and NetworkStream - like if you’re implementing your own network protocol server and client.

See https://learn.microsoft.com/en-us/dotnet/standard/io/pipelin...

The main problem it seems to solve is processing a text or byte range from a stream (be it an endless SSE/WebSocket stream of messages, or just from a huge (multigigabyte+) file on disk - in a non-blocking/async manner.


C# has channels as a library and not a language construct, it lives in System.Threading.Channels namespace.


They do have channels but they don't supply a select equivalent.


Go literally has a Channel class with the same functionality. The major difference is async/await syntax vs goroutine syntax, no?


C# GC, MS and external libraries, build, docs, tools support (especially for Linux) are all worse than Java.


MSBuild is actually really nice, haha. I wish Gradle was that simple.


How so? Linux is a first class citizen in .NET these days.


A first class citzen would have support for MAUI, and something better than VSCode like macOS gets VS4Mac.


JetBrains Rider is available for Windows, Linux, and macOS, and it's much better than VS, VS for Mac, and VSCode.

It's not free, but VS isn't either for any serious commercial usage, and Rider is certainly worth the money.


Workarounds, and still doesn't fix the issues lacking from the company that owns .NET.


What does the company that owns Java offer? Their Java IDE is https://en.wikipedia.org/wiki/JDeveloper and nobody uses that. The latest release came out in 2019. Everyone is using IntelliJ IDEA or perhaps Eclipse.

What does the Python core community offer? IDLE is ugly, is barely an IDE, and didn’t have line numbers until a few years ago. Everyone is using PyCharm or VSCode.


IBM also makes Java, it offers Eclipse.

Sun made Java, it offered Netbeans.

As for the Python community, no one expects great tooling from FOSS languages, as you mention everyone is using tooling from two big corporations.


NetBeans is also still going, although I don't think it has many users.


Does Java even have a MAUI equivalent? There are other open source frameworks for GUI apps. This feels like goal post shifting.


The point is cross-platformness and windows does have MAUI, so not having it on linux means it is not a first-class target.


MAUI is just another workload of the dotnet ecosystem. There is no technical reason for it not to work on Linux outside of someone actually doing so. This has nothing to do with C# at all.


You don’t become a first class citizen overnight. Also, last time I checked .NET lacks something as trivial as an open-source debugger.


It's been many moons, also there is an OSS debugger produced by Samsung.


Except for all ui-only windows-only stuff.


Writing everyday code, Go compiled binaries are on average more performant than C#.


Not really true.

...but the .NET runtime has a higher memory floor. It's a lot easier to write very small apps in Go. New versions are working on stripping in AOT builds but it's still very much a work in progress


> So with this, the last thing Go had going for it over Java is gone, right?

No. Go does containerized microservices better because of its lower memory footprint. If you can use GraalVM then that might not matter.


This is based on outdated advice.

Java stacks like Quarkus [1] have memory as low as 12MB and JVMs like OpenJ9 natively support CRIU for instant startup.

[1] https://quarkus.io

[2] https://blog.openj9.org/2022/10/14/openj9-criu-support-a-loo...


Yet with Go you don't need to worry about comparing Java Stacks, changing your tooling, testing them etc, the binaries "just work" out the box in a near perfect condition.


Not really. We saw overall performance speed up when we limited the number of system threads for goroutines to 1. This was with Docker and Kubernetes (albeit several years ago). This was configured, if memory serves, by an environment variable.

So no, it's not as simple and perfect in my opinion.


Performance increasing when setting `GOMAXPROCS=1` sounds like an (interesting) edge case for the scheduler. If you ever encounter this again, it would be great to file an upstream issue about this. Go has plenty of built in observability tools (I imagine runtime/trace would be good here) so it'd be easy to get the developers the data they'd need.


I believe we tried, actually (this was at ZEIT, before it was Vercel, back when we used k8s to also deploy all of our user docker deployments - which of course wasn't the best idea but it worked for the time being).

I regrettably don't remember the outcome though, and don't use either of them anymore to even test them readily, especially not under the same load.


But that's because Go has a significantly worse ecosystem.

And those Go binaries aren't runnable across a wide range of platforms like JVM apps are.


I suppose it depends on what you're writing. When I read "wide range of platforms" I involuntarily said "who cares" out loud. For those of us (fortunately or unfortunately) working in distributed systems, there is one target environment and maybe a different local environment depending on your setup.

Likewise for "worse" ecosystem. It really depends on your context.

Edit: A closing thought: One thing I find enjoyable about working with Go is that it is tuned for my particular context. I also enjoy writing Java (and more specifically Kotlin) when that context changes (e.g. a desktop application).


You can configure the GC to give the memory back to the OS after a short interval. We do this at FastComments with Shenandoah for our pubsub system.


> Java has an obviously better type system

How does Java have a better type system? Java's generics are unsound, while Go's generics are sound. Java's generics do type erasure, while Go does not. Java's type system is not unified, it does not have a top type (an int is not an Object, int vs. Integer etc.).


Java methods support type arguments. Go methods do not leading to convoluted gymnastics for straightforward behaviour.

> Java's type system is not unified, it does not have a top type

Neither does Go for that matter.

Go's lack of visibility modifiers and package scope namespace cause very common gotchas mentioned in https://itnext.io/we-need-to-talk-about-the-bad-sides-of-go-....

There is a Manning book about Go: https://www.manning.com/books/100-go-mistakes-and-how-to-avo... . And these are not rare mistakes. Everyone makes them and some of those mistakes are repeated again and again in every Go project. (Esp the for-loop ones). I have found programming in Go needing the kind of alert, defensive mindset I adopt for C++ which is quite exhausting. Not so much for Java.

However, to be honest, Rust is probably the only the language where you can relax your "defect-analysis" mind thread while coding - with the exception of async Rust.


Anecdotally, but I feel like Java's type system allows me to write code that gives me more guarantees about the soundness and correctness of my codebase.

One example I can think of is Java's annotations vs Go's closest alternative, struct tags [0], which are just strings added to struct fields that specific libraries can act on; at best these are only checked at runtime, the compiler or type system will not help you with those.

[0] https://go.dev/ref/spec#:~:text=A%20field%20declaration%20ma...


Haskell’s type system does type erasure, is it “worse” for that?


No. Both type erasure and monomorphization have always been legitimate ways to compile generic code. IIRC, the issue with Java is that type erasure is sometimes a leaky abstraction.


What exactly do you mean by "leaky" here? Can you give an example?

My main issue with non-erased generics is that they actually break reasoning wrt. parametric polymorphism as soon as you allow for pattern matching or even just .isInstanceOf on type parameters. Suddenly a function which takes a List<A> can do entirely different things depending on what A is... and that takes away a powerful reasoning tool.


It’s leaky due to reflection, but type erasure is not a problem in itself. .NET did away with type erasure, but it had a cost in terms of language ecosystem of the platform - not erasing List<A> into List will bake the variance of List into the runtime, and thus other languages have to use the same model.


Erasure's not the end of the world, but it'd be nice to have access to the generic class without having to pass it in.

    Collection<Foo> values = ...
    Foo[] array = values.toArray(Foo[]::new);
Not the worst, but not the best either.

There are many situations where the code I implemented could have been far cleaner with reified generics.


Java has records, pattern matching, and proper enums.

As of writing this comment, golang does not allow you to have generics on methods as far as I'm aware.


I think there are still a few changes in the pipe before Java is quite what it could be in terms of performance.

Project Valhalla, and the foreign function and memory APIs in particular.


For non-trivial problems GC performance will be much more important, and Java will beat out go in that category with flying colors.


It is more of generate more garbage / collect more garbage (Java) vs Not generating that much garbage in first place (Go).

This is use case for writing GC benchmarks developers not for typical enterprise CRUD developers.


It's definitely a factor, but it's not really enough. You can squeeze far more performance out of the hardware than idiomatic Java permits.


Sure, but not with another managed language like Go.


I'm not sure what you mean. Why should I settle for less performance than my hardware can deliver?


Are you writing everything with a so-called superoptimizer (because mind you, not even your hand-written assembly will be the fastest, hell, C++/Rust will likely beat it in the general case)? Because it has always been a tradeoff between programmer’s sanity-correctness-maintainability-productivity-performance, at the least. Java does very well on most of it, including performance — it will JIT compile code comparable to C, the reason for the performance discrepancy between the two is memory layout, which may or may not matter for the problem at hand.

In my experience, most (especially business) applications are absolutely not going through millions of data entries doing some local calculations over them, that part is delegated to a database. They either do some IO over them, or have much smaller element sizes.

Java has escape hatches even for handling some of the “millions-of-elements in a hot loop”, but for an AAA game engine it might not be the best choice, but for anything else? It is more than fair game.


Well I regularly do juggle millions of objects in Java. I'd estimate most Java application code is probably running at about a fifth the speed it could be. Modern computers are incredibly fast, but most of that speed is wasted copying data between representations and garbage collecting Stream API debris and boxed integers. Moreover, most Java applications using a database for storage do so in a dumb way that doesn't make good use of either the database or Java.


Well, as I mentioned there are escape hatches, but those come at a price in usually maintainability, e.g. using SoA, or external heap.

If you do have a million objects and it is indeed a performance bottleneck (as shown by the profiler) then it might be worthwhile to pursue these solutions, or even implementing them in another language. Noone claims that Java is for everything, but it is for most things.


I'm not sure Project Panama will have a large performance influence on most Java deployments. Native interop is rare.


Big part of why native interop is rare because it's slow and awful and generally not worth it.

I don't expect a lot of code to actually use these features, but the code that does will be the low level stuff that lets the higher level stuff really perform.


FFI makes cross-platform usage more questionable (have a look at python or node breaking a build on windows vs linux, this is very very rare in case of Java builds), and Java is more than fast enough for most use cases that it simply doesn’t need FFI for speed (like Python for example).

Also, this is a huge advantage of the system, you don’t shallowly depend on a ton of C libraries, you can be confident that your whole application to the last bit is properly abstracted (plus can be debugged and observed with the same great tooling).


Eh, Java leaves quite a lot of performance on the table compared to C or Fortran. It's not as bad as Python, but if you really want to go fast, it's not quite able to compete with fine tuned native code. The Vector API might help a bit, but I don't think it will go all the way.


We'll see how far value objects and the vector API go. The lack of operator overloading makes writing scientific/numerical code a bit awkward, though this could be mitigated by using Kotlin instead.


Native interop is everywhere on Android, but then again, good luck having updated Java features on ART.


This is a pretty terrible comparison, suitable for those X-vs-Y websites. While on paper Go and Java have a lot in common, in practice their philosophies differs enough to produce different enough languages.

I've used both Java and Go and I can tell you, there is no right or wrong but they differ a lot in their ecosystem, tooling and in design patterns. I don't see myself going back to Java because of the virtual threads.


Will they provide automatic M:N multiplexing on OS threads, or is that something that needs to be handled by the user somehow?

Also, what about channels, and select semantics etc?

I think before those are in place, with good library support, it won't be very close to competing with Go.

Not to mention compilation to fully self-contained binaries.


> Will they provide automatic M:N multiplexing on OS threads

Yes, you can set the number of OS threads they can use.

Channels and alia have been implemented in Java’s standard library for many many years, it doesn’t need additional language features.


I mean having a nice feature does not remove all the other pain points. On your last point go mod is way better than anything available in Java. There're still printing out 600 pages book about Maven.

Java will always be a mess of layer of abstraction and magical / heavy framework.


What's the modern startup time of JARs? I haven't used Java in a long time, but long ago, the relatively slow startup time was a dealbraker for using java to write small tools.


Depends what the JARs do of course. Some desirable libraries are unfortunately slow. PicoCLI for argument parsing is one. It's got every feature you might ever want, but, it adds a couple hundred msec to startup.

There is a HotSpot feature called AppCDS. It improves startup time by about 30% in my experiments. However you have to turn it on. Just running `java -jar` won't do it.

Then there is GraalVM Native Image. It can compile JARs to native code ahead of time. They start as fast or faster than the equivalent C program, so it's the big hammer for CLI tools. However, it can't cross compile so you have to compile for the target system on the target system. There can also be compatibility issues with some libraries, though that's getting better rapidly.

So those are the options. Still, even without those extra features, startup time is usually good enough.


> PicoCLI for argument parsing is one

Doesn’t it generate every additional code at compile time? I didn’t notice it slowing down my startup times, it’s mostly every other lib (which you usually have, as what’s the point of a hello world cli app)


I've timed it, it's very slow because it uses reflection to generate the model. The annotation processor doesn't let you avoid this step unfortunately. Native images do.


But that step only has to run as part of building it. It doesn’t have to run at every invocation. How did you try to run it?

From the documentation:

> The picocli-codegen module includes an annotation processor that can build a model from the picocli annotations at compile time rather than at runtime.

> Enabling this annotation processor in your project is optional, but strongly recommended. Use this if you’re interested in

I think you didn’t use it.


I use it but you have misunderstood what it does. It builds a model in memory and then you can use that to do other tasks at build time. It doesn't persist the model in a form that the app itself can use to start up faster.

See here:

https://github.com/remkop/picocli/issues/539

Although looking over that issue, it looks like maybe reflection was never proven to be the source of the slowdowns.


Just tested on an M1 Macbook Pro and it took 148ms

I have a tool I created that creates a git branch based off a Jira ticket number. If I don't supply a ticket number then it errors out straight away, so I think most of that 148ms is the JVM starting up.


On a $50/mo Azure VM running Windows Server I’m able to run a jar via java.exe that prints a string to stdout and exits - in about 160ms end-to-end in a warm-ish environment (Prefetch, IO caching, etc)

Edit: Now it’s down to 80ms - I guess at this scale it’s hard to pin down.


How about 'typical' application code though? The JVM does bytecode verification for every class it loads, for instance.


It loads classes lazily, so your program can start up at `main` very fast, and you only pay for what you use. This might cause some application feature to start up a bit slower on first run, but the byte code format is very compact and can be parsed in a single pass, so I don’t think it would be significantly slower than ordinary machine code loading.


> It loads classes lazily, so your program can start up at `main` very fast, and you only pay for what you use

Right, but large Java programs use huge numbers of classes. They may load thousands of classes just during start-up.

> the byte code format is very compact and can be parsed in a single pass, so I don’t think it would be significantly slower than ordinary machine code loading

That's what I was wondering about. There used to be a flag in OpenJDK to disable bytecode verification to improve start performance, but it was deprecated in JDK 13 [0] and I think it's since been removed entirely. Its removal is understandable but I still wonder about the performance impact.

[0] https://www.oracle.com/java/technologies/javase/13-relnote-i...


The "original billion dollar mistake" is lack of runtime bounds checking (in the creator's words), not the idea of a null/nil/undefined generally.


Kind of, but not quite. There is also:

- compiling to a single binary (I guess jpackage fixes this)

- saner / less elaborate / more ergonomic interfaces to implement and use e.g. compare `io.Reader` (https://go.dev/tour/methods/21) to the Java equivalent


Several commercial JDK options have offered compiling to single binary for 20 years, even if out of reach for common folks. GraalVM and OpenJ9 now make it available as free beer as well.

https://docs.oracle.com/en/java/javase/20/docs/api/java.base...


Is that the modern Java equivalent of the single-method interface in Go? I can see its an abstract class - what do you do if you want to implement both Reader and Writer?


You implement both, duh.

Single method interfaces are what lambdas are for.


Its not an interface though:

  public abstract class Reader
Did Java add multiple inheritance or am I missing something?


The interface you'd want to implement is probably https://docs.oracle.com/en/java/javase/20/docs/api/java.base... (and Appendable for the equivalent to Writer, probably AutoCloseable and Flushable as well.)

However, there's lots of code which just takes a Reader/Writer and then you're SOL.

(In general, the I/O interface/class hierarchy in Java is a bit of a mess because a lot of it was retrofitted to maintain binary compatibility. The equivalent stuff in Guava seems a bit saner, but of course not directly usable with all the 3rd party libraries you might want -- it has shims for the most part, though.)


We used ExcelsiorJET to create (pre-compiled) executables for Java (Swing) applications back in 2009 if memory serves me right.


Yes, unfortunely they went out of business, leaving PTC and Aicas as survivors from those days.


I started with Java and I will most likely never go back. I mainly use Go now.

People want to learn/write Go. It's hard to find anyone that wants to learn/write Java. People probably prefer Kotlin over Java today. It's a much better language and you can keep using JVM libraries.

Go is very simple language that is just as powerful as Java, if not more powerful due to not having to rely on the JVM. That's why it's so popular as a replacement for Java.

How are Jars more portable than Go binaries when you need Java installed to run Jars?

Java obviously has a more mature ecosystem with more libraries due to age, but Go you don't need many libraries to begin with. The Go std lib will give you almost everything you need in most cases.


> Go is very simple language that is just as powerful as Java

I don’t think so, Go is very low on expressivity. Generics help, but I don’t see anything like JOOQ for go, just as an example. Also, no real alternative to Java’s stream api, which can at times make code much more readable than the 4 nested for loops with 4 different exits.



Having looked at the docs, it seems to be full of stringly-typed parameters.


In 2010, I think arguing that relying on the JVM would be an acceptable gripe. However, it is 2023, almost all of your stated issues have been (IMHO quite elegantly) addressed WITH the added benefit (or maybe detriment?) of backwards compatibility in modern versions of Java and the _incredible_ engineering behind the JVM.

I would actually argue the inverse. Between the two languages, Go would be my second choice precisely because it does NOT have the JVM. Even though I have used GraalVM for AOT, with Docker / containerd, I would take the JVM any day. It's just night & day when operating something in production.

That being said, Go still has a lighter resource footprint but I found Go to be a better Python alternative than Java.

Here are some IMHO acceptable gripes with Java:

- Java represents strings using UTF-16 (although there are optimizations introduced in Java9+ already to use LATIN1 / ascii encoding if you don't need to use Unicode)

- Java makes it difficult to have steady state memory consumption (by design)

- Java's escape analysis is primitive

- Java is missing value types (coming soon!)

I think Go is great, but IMHO modern Java is just better at building backend systems.


> How are Jars more portable than Go binaries when you need Java installed to run Jars?

Uh, you don't...?

https://docs.oracle.com/en/java/javase/14/docs/specs/man/jpa...

> you don't need many libraries to begin with.

This really depends on what you're doing. Having many libraries creates a lot of weird potential. You can absolutely subsist on less, but you can do more with more.

A while back I found myself wanting to render MathML into an image. So I dug up jeuclid which is absolutely antique and as far as I can tell not actively maintained. My project flat out would have ended there if I had to build my own math rendering engine to proceed.


> Each format must be built on the platform it runs on, there is no cross-platform support.

So now you're back to platform-specific packages, except you don't have Go's support for cross-compilation. Hardly much of an advantage.


Well yeah. You've still got jars though if you want architecture independence. Java's not really aiming at the desktop market though. It's a server language first and foremost. You're most likely setting up a machine (or VM or container) to cater to the Java code you're running.

But to be realistic, most Java code either runs in application servers (as WARs) or in containers, and in the latter case, even though your java jars are architecture independent, docker just isn't.


Go only does cross-compilation when using the standard library and pure Go libraries, hardly any different.

Plus the target OS needs to exist on the toolchain anyway.


You can make self-updating packages of JVM apps for Windows, macOS and Linux from your local OS ("cross compiled") using Conveyor:

https://conveyor.hydraulic.dev/

It's not JVM specific but it does understand how to bundle the JVM, use jlink to shrink it, read your classpath from Gradle etc.


> How are Jars more portable than Go binaries when you need Java installed to run Jars?

Your java knowledge is half a decade out of date.


It’s not hard to find people who want to write Java. I do it all the time. We have been hiring and writing Java code for fifteen years and have not seen a decline in the interest.


I'm not talking about people that have been writing Java for years. I'm talking about new programmers. If you asked a new programmer out of college which job they would rather have I job programming Go applications or Java. I would bet 90% of them would pick Go.


And they will agains say the current hyped language a year later. It’s almost like we shouldn’t really cater to “new programmers out of college”.


Sorry, it's hard to find you. I had to scroll down this page.

It's even harder to find Rust developers and yet it's well liked - so? So?

The funny part is most of the anti-Java logic is flawed. Maybe using Java will fix that for them :p


It’s hard to find quality people who want to write Java.


Where I am it's hard to find quality people period. If anything targeting Kotlin or Scala developers usually yields slightly better developers but also further restricts an already small candidate pool.


You are talking about more people than there are people in my country (around 10 millions java devs are out there)


I’m sure you can find a hole in your logic. (I’ll point it to you, millions of Java devs are not your hiring pool)


Really? So you mean to say there are no quality people in the millions of Java programmers out there?


No quality people - no, small amount - yes. And millions of Java devs are not your pool.


What is a small amount? 1%? 0.1%?


What people?!? That is a very special bubble over there.


It would be great to have a language that is a love child of Erlang and Rust. All I need is: repl, message passing, actor model, lightweight-threads and strong types. :)


Are you looking for pony (https://www.ponylang.io/discover/#what-is-pony) ? Never tried it, but it does have actors, reference capabilities (in short, "who can get this piece of memory and what can they do with it") but sadly no repl. There is a playground though (https://playground.ponylang.io/).


The love child of Erlang and Rust exists already: Pony.

https://www.ponylang.io

It really is the best of both languages... unfortunately, the main supporter of Pony seems to have stopped using it in favour of Rust though :D.

But if that's really what you want, Pony is your language. It definitely deserves more love.


https://gleam.run is a statically typed language on the BEAM


Scala? A good contender might be OCaml as well.


I wish Swift wasn’t so tied to the Apple ecosystem, as it has (IMO) a good mix of these


I think all of the things you point out are probably true. But I still prefer Go.

And I'm probably not the kind of person you imagine. I'm in my 50s. I used Java for ~20 years. I spent a lot of time developing efficient programming practices in Java and teaching those to both junior developers and people with decades of experience.

I'm also not someone who takes a language switch lightly. It takes much, much longer to become reasonably competent in a programming language and I think that if you are going to choose a "workhorse language", you kind of need to see it as a decade long investment. You don't learn a language in a year.

I switched to Go mostly because it is a nicer language for what I do (servers). But I can fully understand why someone would flee Java. Java has a lot of baggage in the shape of legacy code. When people talk about languages they tend to talk about them as if we all live in a fantasy world where we can write new programs from scratch all the time. But that isn't reality for most programmers. Most programmers aren't starting projects from scratch - they work on stuff that has already existed for a while.

Java has been around for a long time. Which means that every version of the language, every fad, every architectural trend, everything that has happened in almost 3 decades of Java exists at the same time in codebases that are alive and kicking. When developers get a job, this is what they are faced with.

The same is true for C++. Yes, the most recent language spec is a nicer language than what you had 10 or 20 years ago. But that probably isn't the language you get to use when you join a company and start to work on their product.

The reason people choose different languages when they do start from scratch (do a startup) probably isn't entirely rational. I suspect a lot of people avoid languages where they have encountered large legacy codebases full of frustrating code that is done in a out of fashion way.

I chose to switch to Go, despite having 20 years invested in Java, because Go has the features I need in a language and isn't cluttered with much of what I don't need. It has nicer tooling that just works better for everyday things. It also has a healthier approach to how you design stuff: it isn't dominated by large frameworks. I don't have to teach Go programmers minimalism and have "experienced" programmers throw tantrums because they feel threatened when having to re-learn how to program Java.

To be honest, I shudder a bit when thinking about going back to Java. It has so much stuff to deal with when reading other people's code. It doesn't feel like it is worth my time. I can't go back to that. My time is too valuable to me.

(I probably should point out that I did decide that I had better find an alternative to Java when Oracle showed itself to be untrustworthy and litigious. But it took years to find both the opportunity to leave Java and the language to leave Java for)


Can I get a tiny static binary with Java? Nope. Then Go still has legs.

Edit: nvm. You can get static muslc binaries with graal https://www.graalvm.org/22.2/reference-manual/native-image/g...


`com.lol.myapp.factory.UserFactoryImpl.java` would like to have a word about its clear superiority


that's a culture issue that is changing rapidly to avoid that. Also that was common due to previous limitations in the language.

regarding the "com.lol.myapp", i actually think this is a good think for package management, it avoids naming issues with packages for different vendors and forks. On the code itself you should only see these on the import lines on top of the file, which isn't really a big deal.

I think most people issues with java come from old legacy codebases that had over the top patterns like that due to limitations in the language and culture. Nowadays, with a proper conventions guide, java can be quite clean. Sure it won't ever be as clean as something new as Kotlin, as it tries hard to maintain backwards compatibility, so old ugly stuff will remain in the language (even if you don't use it, you might see old code that does), and new stuff designs are restricted by what already exists, but it still has its advantages over new shiny things like kotlin for example: new pattern matching, compile time and compatibility (kotlin "100% compatibility" doesn't actually cover everything, and compatibility is important for Big Co with loads of teams and loads of internal and external dependencies and tools)


> that's a culture issue that is changing rapidly to avoid that.

Define rapidly. It's now been two decades since I was first told "yes, that's a culture thing, but everyone knows it's crazy, and it's on the way out".

E.g., this masterpiece from Benji Smith [0], originally on the old "Joel On Software" forum, is from 2005.

[0] https://gwern.net/doc/cs/2005-09-30-smith-whyihateframeworks...


I you really want to stick to terrible naming and overused architectural principles in 2023 that's your choice but that's hardly a Java issue.


I can’t believe you’ve posted this seriously.


:)


Never wrote one. Couldn't care less.


ALGOL originated null


In ALGOL it was more like a thousand dollar mistake. It didn't become a billion dollars until Java caused NPEs on 3 billion devices


Java's got like the safest most soft-punching NPEs in any language that's got them though. They're so safe you can use them as flow control devices.


yes, but please don't - there's a good dear.


  try { return true; }
  finally { return false; }


I'm pretty sure nulls in C and C++ caused more problems than in Java :)


Java will always have Java syntax, though. And its runtime will remain rather heavy compared to language implementations that compile down to machine code. Go's benefit's will probably always be there.

Java can be taught to 2-year olds so Go seems to have a slight disadvantage there. ;)


Very subjective, but having Java syntax is an advantage compared to goddamn Go — like I have seen my fair share of languages and can generally “read” a new language’s code sample just fine, but go was honestly quite hard the first time with their stupid type, “receiver” etc notation. And I have honestly no problem with Haskell/Lisp/C/etc notation at allz


Java Card on smart cards?


> the billion dollar mistake

Eh, a billion dollars isn't too bad.


"So with this, the last thing Go had going for it over Java is gone, right?"

You can deploy Go app with a single binary, no runtime required. No runtime configuration.


Nowadays you don't deploy a global JRE on machines anymore. Each application has its own JRE bundled.


This is missing the forest for the trees.

Languages are very complex, you will not get a good picture of their overall strengths and weaknesses if you limit yourself to a very zoomed-in view, like comparing a few features of the languages themselves.

As an example, consider the digital camera market vs the cameras in smartphones.

The digital camera is a clear winner! So many features. The quality of photos taken is objectively better in every metric too.

And yet the digital camera market is dying, completely killed by smartphones.

But why, the features are cleary better right?!

Because we're looking at it wrong, we're tunnel-visioned on the "camera" part. We need to look at what actually matters.

Thankfully, that's simple to answer and is the same for every product in existence. All that matters is the user experience.

Nobody wants extra features on their camera, nobody wants a camera in the first place, nobody wants to take photos either. What people actually want is to preserve the moment of their first born child taking their first steps. The technology is irrelevant as long as it lets the user do what they really want.

Why would I want a digital camera when my smartphone has a decent enough one that is effectively free and always available? A low quality camera on you is infinitely more valuable than the professional camera at home.

Programming languages are no different, just that "users" in this case are programmers, which seems to be confusing to some.

What is the developer experience of using Java vs Go? - that's the real question you need to answer to get to the bottom of this.

Start a new project and write some code in both languages, compare the experiences. Be wary of biases, if you have pre-existing experience in one of the languages it's going to shadow your judgement (the curse of knowledge). Pay attention to aspects of good design - how many pointless decisions do you have to get through before actually shipping code?

Go eliminates whole areas of pain points:

- Dependency management? Go modules.

- Tests? Built-in.

- Code formatting? Built-in autoformatter.

- Compilation time? As fast as it gets.

- Distribution? Single binary.

- Writing code? Minimal ceremony, just make a function.

- Performance? The idiomatic code you write will naturally perform well due to value types, explicit pointers, a culture of straightforward code with no needless indirection. If you need to optimize, the profiling tooling is great and the optimizations themselves straightforward due to intuitive language and GC semantics - just reduce allocations.

- GC? A single implementation, good enough for 99% of cases. Two whole knobs available if you really need to tune it. Performs well due to the language not getting in its way.

- Standard library? Excellent, good balance between batteries-included and bloat. The built-in HTTP server is suitable for 80%+ of workloads.

- Concurrency? Core to the language - syntactically supported green threads. The entire language and its ecosystem are built with it in mind.

- Linting? Community-made linter runner with a curated list of good linters.

- Found a bug in a library? No problem, the library is written in straightforward Go, the same flavor of Go you've been writing. It's of course autoformatted as well.

There you go, a single go-to solution to each problem. The language's designers have gotten all the pointless details out of the way for you. You get to focus on writing code.

Now compare the above points with Java.

Go was designed for developers, with the same philosophy Steve Jobs designed Apple products for users. Java wasn't. Simple as.


Go doesn't have exceptions. That makes it infinitely better than Java could ever be.


> Java's jars are more portable than go's binaries

Since when?


Well a go binary only runs on the platform it was compiled for. A jar file runs on any platform that has a working JVM. I.e. most commonly used operating systems and CPU architectures you can name.

Of course whether that matters to you is another thing. Most people package either of those up using docker and run them on generic linux hosts.


Since forever.


A few counterpoints:

- Modern Java requires heavy use of Decorators

- JSON handling is tiresome

- As soon as you are using Spring you aren't actually coding Java anymore

- Many mature libraries have dated APIs

- Similar functionality is often implemented multiple times in multiple different libraries. Can be confusing if you start out.

- Java requires a well configure IDE

  - However, IDE Support won't matter in a year or two when we have IDE integrated AI support
I guess it depends on the problem you want to solve. For the use cases I work on I'd prefer Go for its simplicity.


> As soon as you are using Spring you aren't actually coding Java anymore

Can you expand on that. I'm pretty sure its still Java. There are some additional annotations that help with autowiring and object reuse, but probably affect the code less than Lombok.


> For the use cases I work on I'd prefer Go for its simplicity

Why don’t “enjoy” the simplicity of assembly then? (Not trying to be sarcastic, just I always felt that this logic is flawed. Especially that java is a very simple language, with very few concepts. If you don’t like, you absolutely don’t have to use metaprogramming like Spring)


Yes, Java is a simple language. But all the stuff that was added to Java makes it at times feel bloated and annoying to work with.

> Why don’t “enjoy” the simplicity of assembly then?

Now thats just stupid.


What Go has going is that it's faster and more pleasant to develop in. Yet another bullet-point-feature bolted on only makes the Java ecosystem jungle worse in that aspect.

I find your other points a bit dubious. Java's type system suffers from extreme verbosity and little soundness, and exceptions have not been a successful error-handling story. I don't think you can call a jar that needs a system installation of some specific jre portable - at all. The ecosystem may be huge, but also nightmare in terms of interoperability and support of newer features. IDE click-driven-development is a crutch for the extreme verbosity and complexity of patterns in Java. Compile times are much worse in my limited experience.


Based on your writing, your experience with Java is indeed very limited and not up-to-date at all.


When talking about languages a lot of people talk about languages as if all programmers deal with is starting new projects from scratch and being able to use the latest version of the language, using current practices. For most programmers this isn't the case. Most programmers work on projects that were not started by them, and have been around for a while. Or software that is built on, or has to be closely integrated with, legacy code.

Based on your writing, you sound like either a junior engineer, or someone who has had very little responsibility for making resource allocation and strategic decisions.


It's simply that the average Java project is out of date and not worth updating, which says a lot.


As in works just fine and generates business value despite not being touched in years? Yes, says a lot.


As in the effort to update or just change anything major is similar to a rewrite.


Are you kidding me? If it wasn't for large enterprise corporations Java would be long dead by now. Ever since Oracle took over Java has seen almost no improvements. The logical comparison is Java to C#, and C# has seen _a lot_ of improvement over the last 7 years or so. It just so happens that Java is sometimes unavoidable for Android development were Kotlin is not (yet) possible.

Java is, like its large corporate users, stuck in the past.


We are at Java 20, when Sun died we were stuck in Java 6.

No improvements, what a joke.


Version numbers are extremely arbitrary, and Java has a new number every 6 months.

If you compare Java 6 (2006) and Java 20 (2023), and then look at C# 3.0 (2007) versus C# 11 (2022), C# has gained many more features and improvements than Java did over the years.


Gaining features without bounds is not a positive in case of a language. C# is a very good language, but they really do copy C++ in adding everything under the Sun to it, and the complexity of managing it all can easily crumble under even good developers.

Java may be the other side of it, but I think it is a safer bet.


Including copying Java features like tiered compilation, default interface methods, compiler plugins, AOT compilation, being able to run UNIX systems, failing on having phone OS written in C#.

Anyone that misses C# on the JVM can use Kotlin or Scala.

And best of all, due to Microsoft's lack of investment on VS4Mac and VSCode versus VS proper, the best .NET IDE outside Windows runs on Java/Kotlin.


Some features were introduced in Java before they appeared in C#. But many C# features are yet to be seen in Java.

> best .NET IDE outside Windows runs on Java/Kotlin.

I would argue “in the world”. And it does not matter in the slightest to a user. The best Python IDE is PyCharm, also written in Java/Kotlin. I don’t know which PHP IDE is the best, but I’m pretty sure none of them are written in PHP, and the same probably applies to Ruby.


And many more are introduced into the JVM before they make their way into CLR.

As for mattering to the user, it surely does, it shows how lacking their ecosystem is for specific workloads.


> Ever since Oracle took over Java has seen almost no improvements.

Is this supposed to be sarcasm? Because the literal opposite is true.





Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: