I'm a fan of Go, but I don't think it's the product of some awesome collective Google wisdom and experience. Had it been, I think they'd have come to the conclusion that statically eliminating null pointer exceptions was a worthwhile endeavor, just to mention one thing. Instead, I think it's just the product of some people at Google making a language they way they wanted to.
Go is the product of like 3 Googlers' tastes. It isn't some perfect answer born out of the experience of thousands of geniuses.
I think they got a lot right - fantastic tooling, avoiding glibc, auto-formatting, tabs, even the "no functional programming so you have to write simple code" thing is definitely a valid position. But I don't think anyone can seriously argue that Go's handling of null is anything but a huge mistake.
For example, Ken Thompson has said his job at Google was just to find things he could make better.
Go has a very low barrier to entry, but also a relatively low ceiling. The proliferation of codegen tools for Go is a testament of its limited expressive power.
It doesn't mean that Go didn't hit a sweet spot. For certain tasks, it very much did.
Well, that point in the design space was already occupied by Java which also has extremely fast builds. Go exists primarily because the designers wanted to make a new programming language, as far as I can tell. It has some nice implementation aspects but it picked up its users mostly from the Python/Ruby/JS world rather than C/C++/Java, which was the original target market they had in mind (i.e. Google servers). Scripting language users were in the market for a language that had a type system but not one that was too advanced, and which kept the scripting "feel" of very fast turnaround times. But not Java because that was old and unhip, and all the interesting intellectual space like writing libs/conf talks was camped on already.
Java programs rely on the JVM, of which there are many variants. Run time options are often split into multiple XML files -- one file for logging, another to control the number of threads and so on. Checking for the running process using `ps | grep` yields some long line that wraps the terminal window, or doesn't fit neatly into columns shown in `htop` or `btop`.
These complaints are mostly about conventions and idioms, not the languages themselves. I appreciate that the Java ecosystem is extremely powerful and flexible. It is possible to compile Java programs into standalone binaries, though I rarely see these in practice. Containers can mitigate the messiness, and that helps, up until the point when you need to debug some weird runtime issue.
I wouldn't argue that people should stop programming in Java, as there are places where it really is the best choice. For example deep legacy codebases, or where you need the power of the JVM for dynamic runtime performance optimizations.
There are a lot of places where Go is the best choice (eg. simple network services, CLI utilities), and in those cases, please, please deploy simple Go programs. Most of the time, developers will reach for whatever language they're most comfortable with.
What I like most about Go is how convenient it is, by default. This makes a big difference.
And you left out classloader/classpath/JAR dependency hell, which was horrid circa late 90s/early 2000s...and I'm guessing was still a struggle when Go really started development. Especially at Google's scale.
Don't get me wrong, Java has come a long way and is a fine language and the JVM is fantastic. But the java of 2025 is not the same as mid-to-late 2000s.
But Go wasn't designed for CLI apps. It was designed for writing highly multi-threaded servers at Google, according to the designers, hence the focus on features like goroutines. And in that context startup time just doesn't matter. Startup time of servers at Google was (in that era) dominated by cluster scheduling, connecting to backends, loading reference data and so on. Nothing that a change in programming language would have fixed.
Google didn't use classloader based frameworks so that also wasn't relevant.
For example initial JIT caching experiments on OpenJDK came from J/Rockit.
I'm also not fond of any of the Golang syntax, especially not having exceptions. Or if you want explicit errors, fine, at least provide nice unwrap syntax like Rust does.
I had to, to make sense of whatever was going on.
If this is systems programming, yeah you're reasoning about channels, but that's mostly the same in any language. Golang has slightly nicer support for that if anything.
And with the increasing performance of Bun, it seems that Go is about to get a whooping by JS.
(Which isn't really true, as most of the Bun perf comes from Zig, but they are targeting JS Devs.)
None of these runtimes make JS anywhere even close to single-threaded Go perf, let alone multithreaded (goroutine) perf.
npm was also maybe the first default package manager that "just works," unlike Python or browser JS.
I need none of that guarantee and all of the compilation speed along with a language where juniors in my team can contribute quickly. Different problem space.
> Types either represent the data or not
This definitely required, but is only really the first step. Where types get really useful is when you need to change them later on. The key aspects here are how easily you can change them, and how much the language tooling can help.
As you go further towards formal verification you get:
* More accurate / tighter types. For example instead of `u8` you might have `range(0, 7)` or even a type representing all odd numbers.
* Better compile time detection of bugs. (Ultimately you are formally verifying the program.)
* Worse type errors. Eventually you're getting errors that are pretty much "couldn't prove the types are correct; try again".
* More difficulty satisfying the type checker. There's a reason formal verification of software isn't very popular.
So it's definitely true that "more obnoxious types" exist, but Go is very far from the obnoxious region. Even something like Rust is basically fine. I think you can even go a little into dependent types before they really start getting obnoxious.
TL;DR, he's just lazy and doesn't really care about bugs.
No. two types can represent the same payload, but one might be a simple structure, the other one could be three or twenty nested type template abstractions deep, and created by a proc macro so you can't chase down how it was made so easily.
Nowadays the culture seems to have evolved a bit. I now go into high alert mode if I see a channel cross a function boundary or a goroutine that wasn't created via errgroup or similar.
People also seem to have chilled out about the "share by communicating" thing. It's usually better to just use a mutex and I think people recognise that now.
I'd be interested in this project if you do decide to pursue it.
Something to look into in retirement.
At the same time, I have worked at places where people had to rewrite major parts of their backend in other languages because Python/Node was no longer sufficient.
It's just a project from a few very talented people who happen to draw their salary from Google's coffers.