
Go: The Good, the Bad and the Ugly - pjmlp
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/
======
jordz
I don’t think it’s fair to say they ignored advances in modern programming
languages. There are opinionated reasons for the omission of generics and they
do make sense in the overall architecture of the language. At the end of the
day it’s about trade offs and ultimately my opinion is that, having written a
lot of both C# and Go, I can see the pros and cons of feature sets. Its about
choice of tool for the solution in mind. I’m happy with that.

~~~
chisleu
Absolutely! The "advances" were design decisions that go chose not to use. The
people who designed golang are not newbies. They didn't ignore anything, nor
were they ignorant of other languages. They had good reasons for choosing to
go another way that makes sense for their language.

The end result is, in my humble opinion, a very stable language that is very
high performance, and is highly maintainable. It's not perfect. It's not great
for every use. Java does some things better. C does some things better, etc...
It just fills a niche that was unfilled in an opinionated manor.

~~~
collinf
I think one thing that people get caught up in with Go as well is... well it's
stupid easy to learn and work with.

I work for a giant enterprise company and the range of programming talent that
we have is incredible. I know some people that couldn't code there way out of
fizbuzz. Unfortunately, getting these folks onboard to learn the functional
abstractions doesn't always sell (especially to the biz folks).

One of Go's biggest strengths is that anyone can pick up the language and be
somewhat productive in it after a week.

My 2 cents in the bottomless well of programming language opinions.

~~~
__sr__
> One of Go's biggest strengths is that anyone can pick up the language and be
> somewhat productive in it after a week.

And then what? They find themselves duplicating the same code again and again
because Go doesn’t provide good abstractions to reuse code. Or that they write
a ton of code to do what could be achieved in a few lines in a reasonable
language.

You learn the language in a week because there is not a lot to learn. Shell
scripting is simple to learn as well, but nobody is building anything beyond
short scripts using it.

An ideal language should allow the user to be a productive after a short time,
but should have have enough power for advanced users as well.

One of the things I hate the most about Go is that source code generation is
considered an acceptable solution to many problems. Perhaps I should go back
to C — the preprocessor can be used to generate code as well as anything. In
fact, the predecessor to C++ was a preprocessor used to generate C code that
simulated C++ like behaviour.

~~~
flavio81
>They find themselves duplicating the same code again and again because Go
doesn’t provide good abstractions to reuse code. Or that they write a ton of
code to do what could be achieved in a few lines in a reasonable language.

This.

>You learn the language in a week because there is not a lot to learn.

Exactly. For example, Brainfuck is even simpler and easier than go. It also
produces even worse, unmaintainable, horrible code.

------
laumars
Opinions on programming languages are like arseholes in that everyone has one.
So I'm far less interested in hearing people intellectualise over what they
perceive to be good or bad qualities of a particular language (otherwise known
as arguing personal opinion as irrefutable fact) and far more interested in
seeing what what cool projects people can make. After all, many of us managed
to write some pretty awesome stuff in BASIC back in the 70s and 80s so there's
really no excuse these days given the tooling and hardware we now have
available.

~~~
Cyph0n
So the fact that Go provides no solid dependency management solution is
"personal opinion"?

If everyone followed this approach, we would never have ended up with amazing
languages like Rust. Why try to improve C++ if you can build cool stuff with
it, right?

~~~
czbond
What do you mean by "no solid dependency management"? Do you mean out of the
box? This used to be true, but with glide and dep
([https://github.com/golang/dep](https://github.com/golang/dep)) it seems to.
It is not npm or bundler - but progressing.

~~~
chisleu
Yes, go decided to wait to let the various competing systems duke it out in
much the same way that node.js left it out for a while.

I use git as my dependency management. I pull a specific version of a golang
dependency and put it into my private git repository. Then i don't have to
worry about someone's github getting hacked, repositories getting deleted,
etc...

Those problems have all happened with npm and github in the node ecosystem. Go
solves these problems by allowing you to decide on how to manage your
dependencies.

~~~
iends
This is no longer true. The core team proposed vgo a few weeks ago, after the
majority of the community solidified around godep.

------
zamalek
> The standard JSON encoder/decoder doesn't allow providing a naming strategy
> to automate the conversion

This is dangerous. You should be tagging all your fields even if the name
matches exactly because, especially if inconsistently applied, someone might
not realize that they are changing public schema with a refactor. If tags are
completely missing in your codebase, you have to research every single type to
determine if it gets serialized to JSON somewhere (good luck).

Avoiding magic is a feature.

~~~
TheDong
Speaking of magic, json tags are weird stringy magic only used at runtime via
reflection.

If I have a struct with the tag `josn:"foo"` instead of "json", I won't get an
error, it'll just silently blow up.

If I add a new field, I have to manually add the tag too.

You know what's both less magic and less fragile? The rust equivalent.

In rust, I write '#[serde(rename_all = "camelCase")]' above a struct. If I
typo, it won't compile. If I add new members to the struct, they'll work
without me having to remember some boilerplate.

If I have one that needs an exception, I can put '#[serde(rename = "foo")]'
above just that one. It also won't compile if I typo.

This is the alternative to what go has; compile-time safety, language-features
that let a library provide such naming strategies, etc. It's less magic since
it doesn't rely on these weird conventionally named tag things that are only
sorta part of the language.

I suspect what the author meant by naming strategy was something like that.
Your argument that the author is adverse to being explicit is a strawman; the
author appears to be asking for a struct-wide way to be explicit.

~~~
frankietwenty9
> Speaking of magic, json tags are weird stringy magic only used at runtime
> via reflection.

Annotations are used at run-time via reflections. JSON marshaling is a common
use for annotations.

> If I have a struct with the tag `josn:"foo"` instead of "json", I won't get
> an error, it'll just silently blow up.

well it won't blow up, it won't function as intended - much like a logical
error. A linter, like the one that comes with VSCode will catch this error for
you.

Annotations don't need to conform to any format and are not compile time
errors. I am unsure how this could change while maintaining the Go1 guarantee.
Perhaps this could be an area of improvement going forward - having typed
annotations.

> If I add a new field, I have to manually add the tag too.

It will work in go too. If you want it to deviate from the naming of your
struct field, you would need to describe the map. It's like complaining that
when you add a field to your database, you need to add it to your code too.
Sure it's not as powerful as Rust, but that's the point.

> In rust, I write '#[serde(rename_all = "camelCase")]' above a struct. If I
> typo, it won't compile. If I add new members to the struct, they'll work
> without me having to remember some boilerplate.

That's handy, but incompatible with Go 1. It is worth noting that Go and Rust
are different. Rust is more focused on power, while Go has more of a focus on
simplicity.

~~~
jmhain
> Go has more of a focus on simplicity.

But specifically on simplicity of implementation. Which often means pushing
the complexity onto the developer.

This line from the article is a pretty good summary of Go in general:

> It looks to me a like a big flaw in the language design to make its
> implementation easier.

~~~
frankietwenty9
> But specifically on simplicity of implementation

I'd suggest a simple language requires a simpler implementation.

> It wasn't enough to just add features to existing programming languages,
> because sometimes you can get more in the long run by taking things away.
> They wanted to start from scratch and rethink everything. ... [But they did
> not want] to deviate too much from what developers already knew because they
> wanted to avoid alienating Go's target audience.[1]

Sometimes for simple problems a simple tool is the best fit.

[1][https://arstechnica.com/information-technology/2009/11/go-
ne...](https://arstechnica.com/information-technology/2009/11/go-new-open-
source-programming-language-from-google/) (via wiki)

------
tptacek
Buffering channels doesn't avoid deadlocks. As I understand it: buffering is a
performance feature; if your code isn't correct without buffered channels, it
isn't correct.

~~~
4ad
Not true. Buffered channels are able to express semantics impossible to
express with unbuffered channels. E.g. a counting semaphore. The most common
buffered channel buffer size is one, and the code would not be correct with
either zero, or more than one. Using buffered channels in Go for their
semantics instead of their performance is a very common idiom in Go. Much more
common than adding buffering to channels as an optimization.

~~~
jerf
Fair enough. But it is important for people to realize that buffered channels
aren't "async" channels; they're async until they fill up, at which point they
resume blocking until there is a slot available. If you have some sort of
channel network setup that is deadlocking with unbuffered channels and you
"fix" it by adding some buffering in, unless you've got a very solid analysis
as to why that buffering is correct, you haven't fixed it, you've just delayed
the problem until load is higher.

It's in many ways academic anyhow. Most of the tricks I've seen for buffered
channels are better done some other way, and in practice I almost always use
unbuffered channels. Most of the time that someone thinks they want a buffered
channel because of performance issues, they're actually exactly wrong and
backwards... if there is some sort of mismatch between the speed of the
consumers and producers you generally _want_ the coupling introduced by
unbuffered channels, even if it is counterintuitive.

~~~
4ad
Yes, buffered channels should almost never be used for performance reasons.

Buffered channels should be used for their semantics, but in general
unbuffered channels are preferred to buffered channels if possible because
buffered channels cause combinatorial explosion of state, and are hard to
reason about accurately.

------
callesgg
I agree with this article to a surprising degree.

Generally i find that people complain about other things that i don't care
about but this article was spot on when it comes to my believes.

------
VyseofArcadia
> Up to recently there wasn't really an alternative in the space that Go
> occupies, which is developing efficient native executables without incurring
> the pain of C or C++.

That's just plain not true. There are so many languages that compile to
machine code. So, so many.

~~~
askthrowaway
Can you list some of these ?

~~~
pjmlp
Additionally, OCaml, SML, Free Pascal, Delphi, Oberon, Oberon-2, Oberon-07,
Active Oberon, Component Pascal, Basic, Common Lisp, Scheme, Java (yes all
commercial JDKs do support it and on Android 5+), Clipper, Modula-2,
Modula-2+, Modula-3, Dart 2.0, Swift, MLton.

It just happened that with widespread adoption of C and C++, many devs lost
sight of alternatives.

~~~
nostalgeek
You are ignoring Ada. In fact most problems with the Go languages are solved
in Ada(tasks vs goroutines and channels, generics vs interface {},
synchronized data structures, variants type vs runtime plumbing to create
unions...).

~~~
pjmlp
No I am not, remark the _Additionally_ at the beginning of the setence and
eesmith's comment already mentioning Ada with the same time as mine.

Ada is one of my favourite languages.

------
mfer
One of the changes no one seems to be talking about is in leadership. Rob,
Robert, and Ken would agree on a feature going into Go before it did. Now Russ
Cox is in sole control of the leadership. This is happening at a point when
work such as Go 2 and vgo are happening.

~~~
ngrilly
> Now Russ Cox is in sole control of the leadership.

What makes you think that? As far as I can see, Rob, Ian, Brad, Andrew and
many others are still in the decision loop.

------
thedatamonger
From the horses mouth (Rob Pike) on why they chose to do what they did in Go.

[https://www.youtube.com/watch?v=rFejpH_tAHM](https://www.youtube.com/watch?v=rFejpH_tAHM)

------
logicallee
I thought this was a superlative article, that mentions many gotchas that I
haven't seen collected into one place in this way.

I strongly recommend it.

------
nouney
Just another guy speaking subjectively of something he barely know.

~~~
chisleu
"barely know" isn't the case here. The guy seems to have a solid
understanding. I certainly don't agree with his opinion on golang, and golang
is still a fantastic choice for many of my uses.

~~~
jrs95
Yep. I don’t agree with his opinion, but he probably doesn’t agree with mine
about Rust: I’d rather gouge my eyes out than use that shit for anything other
than a C/C++ replacement.

