Author here: I wrote this in 2020, have changed jobs twice since. Both jobs involved Go in some capacity, where it's supposed to shine (web services). It has not been a pleasant experience either - I've lost count of the amount of incidents directly caused by poor error handling, or Go default values.
If folks walk away with only one new thought from this, please let it be that: defaults matter. Go lets you whip something up quickly, but making the result "production-ready" is left as an exercise to the writer. Big companies that have adopted it have developed tons of tooling around it, use all available linters, do code generation, check the disassembly, and regularly pay the engineering cost of just using Go at all.
That's not how most Go code is written though. I'm interested not in what the language lets you do, but what is typical for a language - what is idiomatic, what "everyone ends up doing", because it is encouraged.
Because that's the kind of code I inevitably end up being on-call for, and I'm tired of being woken up because of the same classes of preventable errors, all the time. It doesn't matter that I don't personally write Go anymore: it's unescapable. If it's not internal Go code, it's in a SAAS we pay for: and no matter who writes it, it fails in all the same predictable ways.
Generics will not solve this. It is /neat/ that they found a way to sneak them into the language, but it's not gonna change years of poor design decisions, and it's definitely not gonna change the enormous amount of existing Go code out there, especially as the discourse around them not being the usability+performance win everyone thought they would be keeps unfolding.
As I've mentioned recently on Twitter, what makes everything worse is that you cannot replace Go piecemeal once it has taken hold in a codebase: its FFI story is painful, the only good boundary with Go is a network boundary, and there's often a latency concern there.
Lastly: pointing out that I have been teaching Rust is a lazy and dismissive response to this. For me personally, I have found it to be the least awful option in a bunch of cases. I am yearning for even better languages, ones that tackle the same kind of issues but do it even better. I like to remind everyone that we're not out there cheering for sports team, just discussing our tools.
If you're looking to reduce the whole discourse to "X vs Y", let it be "serde vs crossing your fingers and hoping user input is well-formed". It is one of the better reductions of the problem: it really is "specifying behavior that should be allowed (and rejecting everything else)" vs "manually checking that everything is fine in a thousand tiny steps", which inevitably results in missed combinations because the human brain is not designed to hold graphs that big.
Don't fall prey to an ad-hominem argument - I don't think your article negatively hints at any kind of 'this is a Rust fanboy-made praise text' and it saddens me that a genuinely legit article like this needs to have the author defend himself like this.
Your points were well explained. Go has several serious warts which, in my own opinion, are showstoppers, and you are comparing it to a language which is somewhat newer and had more time to mature and of course, learn from other designs and their decisions as well.
Generics were intentionally left out, for example, because of the fear-mongering claim that "if you have generics your code is going to automatically become the C++'s STL at some point". After many years, the lack of even minimal generics got so bad they figured they'd start to lose share to rising languages like Nim, Rust and even the old grandpa C#, now portable everywhere with the Core stuff. It is very clear how badly bolted and rushed generics were in Go.
Rust has its warts as well and the language spec is already starting to become somewhat... large. We need to be careful not to invent C++2.0 right? ;-)
Anyways, just wanted to say this to you in hopes it'd lighten the mood and validate that you are in a good path with these articles. It was a very insightful reading for me. Thank you.
> Rust has its warts as well and the language spec is already starting to become somewhat... large
It doesn't have a spec, so we don't know how gigantic it would be.
> It doesn't have a spec, so we don't know how gigantic it would be.
While it's not a formal spec, there is https://doc.rust-lang.org/reference/index.html which can give an idea of the size a Rust spec would have.
Tone matters a lot. Just being strictly, technically correct does not get much far, specially in these times. Its funny that author put in scathing, personalized review of Go but then act surprised that some Go fans took that personally.
At least in Go case it is just random Go fans dismissing criticism. Go authors do not jump in to defend language at every internet posting. This is absolutely not the case with Rust where committers just pop-in everywhere language/ecosystem is criticised.
>It is very clear how badly bolted and rushed generics were in Go.
They were designed with the help of type system experts like Phil Wadler: https://arxiv.org/abs/2005.1171
Not sure why you think they were 'rushed', given the timeframe involved.
I don't follow. The Arxiv link you provided lacks a single mention to the name you mentioned... and even without knowing him, I can believe you that Mr. Wadler is probably a prominent and important type-theory researcher. But unless your argument is about a reverse ad hominem (citing an important name for the sake of "important person participated in this, cannot criticize design/execution"), I cannot see the point of this at all.
Explaining further: When I mentioned "rushed", it was clear for most of us that "generics" appeared quite late in the Golang picture, and it did only rise in priority when they figured they were losing market share to 15yo+ languages because of this single design flaw. Also, as soon as it became a heated topic, the working group started racing to have it included as soon as possible. As early as Go 1.17. Then, in the last moment, it got pushed to 1.18 because several deep questions remained open. Why the need to race it so much for .17, I ask? They probably felt it was 'too late' so must get it out of the door as soon as possible, even build hype for it on .17 in several changelogs even though it was clearly not ready (and those who were following the working group discussions know that it would NEVER be done for the .17 release).
Hope I was able to shed some light on the reasoning and word choices I had in my previous answer.
>Then, in the last moment, it got pushed to 1.18 because several deep questions remained open.
Delaying a feature to make sure that it's implemented correctly is the exact the opposite of rushing it!
I think you are vastly overestimating the influence of internet message board drama on the actions of the Go core team. In any case, you provide no evidence to support your claims about their motivations.
I'm not sure what happened with that Arxiv link, but here is another link to the paper I was thinking of: https://homepages.inf.ed.ac.uk/wadler/papers/fg/fg.pdf The fact that a leading researcher in type systems was a key participant in the effort suggests that this was a carefully thought through proposal (not necessarily perfect or beyond criticism, but not a rush job). Of course, if you think otherwise, you can point to any specific flaws in the paper.
>reverse ad hominem
The term you're looking for is 'argument from authority'. But argument from authority isn't really a fallacy. It's perfectly sensible to trust authorities, within reasonable limits.
I definitely went into the article thinking you have to be an idiot, but you exposed a lot of reasonable issues that can certainly bubble up depending on the type of application you are working on.
I primarily work on web servers but even I've noticed a handful of the issues in present. For example, the net/http package is not all that great, in my mind. It has a lot of downsides, and the timing out section really calls this out quite well. The native functions pretend to have sensible defaults, but in any production-ready application those are all discarded and you'll need to create custom clients to handle the different types of requests you are making.
I will say I'm a fan of Go. I think it has made my life much simpler in that it was probably the first language to expose me to the world away from "magic" - with Java being my first language, frameworks are far too rampant and Go helped de-mystify a lot of that for me.
But as I've progressed to learn other languages, I think this is less a strength of Go and maybe moreso just a negative of Java/Ruby/Python oftenntimes. I've not played around much with Rust but I've heard great things.
As much as I wanted to think this was just a Rust fanboy attacking Go, I think your article was quite well written - and also helped me question "why do I even care?" I didn't write Go, I just happened to land a job that uses it where I excelled well, and I think in my early career that's largely because Go is extremely beginner friendly. It's a tool disjoint from me - I wouldn't be upset at someone if they told me they don't like hammers made by DeWalt, because it's not an attack on me. Same with Go, or any other tool we use to do our jobs.
Now that I'm a much more senior engineer I find I use Go out of convenience purely due to expertise, but I've been itching to work with something new. I think for my next side project I'll take a stab at Rust.
Anyway - great article! I'm happy I read it (even though in the beginning you tell me not to, hehe).
Thanks for your amazing articles, Amos!
> I'm interested not in what the language lets you do, but what is typical for a language
This is the crux of the problem. To step away from Go/Rust and pick on another language, one could argue that Python lets you annotate every variable for some linting checks, but that doesn't mean they all are. This leads to horrible time-sinks where someone accidentally adds a comma to the end of a line, turning a scalar into a 1-tuple. I know folks to whom this has happened and who burned a half day trying to track it down.
I personally get annoyed by the "Language X gives me the freedom to do Y." I find that I and a lot of my peers often prefer constraints imposed (instead of freedom given) by the language as a way of preventing countless issues at runtime.
> its FFI story is painful
Is the Rust FFI story much better?
Serious question: I have a Go code base with performance-sensitive inner loops. Considering options now for the longer term.
> Is the Rust FFI story much better?
So very much so. All the usual C types are available, you even have as much control as even saying that you want to struct laid out as it is in C, doing something as simple as binding a function is just declaring the function like you would in C. As it uses llvm it can even inline across C code, optimize and all just like they were written in the same language if you wish to do that.
I think you can add this to your original post.