1. 122

  2. 85

    I love that this post is called “I do not like Go”, not “Why you should stop using Go”, “Go considered harmful” or any of the other “here is my opinion, treat is as fact” titles.

    1. 47

      Half this article is out of date as of 2 days ago. GOPATH is mostly going to die with vgo as is the complaint about deps.

      Go is kind of an example of what happens when you focus all effort on engineering and not research.

      Good things go has:

      • Go has imo the best std library of any language.
      • Go has the best backwards compatibility I have seen (I’m pretty sure code from go version 1.0 still works today.).
      • Go has the nicest code manipulation tools I have seen.
      • The best race condition detector tool around.
      • An incredibly useful in practice interface system. (I once used the stdlibrary http server over a serial port because net.Listener is a simple interface)
      • The fastest compiler to use, and to build from source.
      • Probably the best cross compilation story of any language, and uniformity across platforms, including ones you haven’t heard of.
      • One of the easiest to distribute binaries across platforms (this is why hashicorp, cockroachdb, ngrok etc choose go imo).
      • A very sophisticated garbage collector with low pause times.
      • One of the best runtime performance to ease of use ratios around.
      • One of the easier to learn languages around.
      • A compiler that produces byte for byte identical binaries.
      • incredibly useful libraries maintained by google: (e.g. Heres a complete ssh client and server anyone can use: https://godoc.org/golang.org/x/crypto/ssh)
      • Lots of money invested in keeping it working well from many companies: cloud flare, google, uber, hashicorp and more.

      Go is getting something that looks like a damn good versioning story, just way too late:

      Go should have in my opinion and order of importance:

      • Ways to express immutability as a concurrent language.
      • More advanced static analysis tools that can prove properties of your code (perhaps linked with the above).
      • Generics.
      • Some sort of slightly more sophisticated pattern matching .

      Go maybe should have:

      • More concise error handling?
      1. 53

        I have been involved with Go since the day of its first release, so almost a decade now, and it has been my primary language for almost as long. I have written the Solaris port, the ARM64 port, and the SPARC64 port (currently out of tree). I have also written much Go software for myself and for others.

        Go is my favorite language, despite everything I write below this line.

        Everything you say is true, so I will just add more to your list.

        My main problem with Go is that, as an operating system it’s too primitive, it’s incomplete. Yes, Go is an operating system, almost. Almost, but not quite. Half and operating system. As an operating system it lacks things like memory isolation, process identifiers, and some kind of a distributed existence. Introspection exists somewhat, but it’s very weak. Let me explain.

        Go presents the programmer with abstractions traditionally presented by operating systems. Take concurrency, for example. Go gives you goroutines, but takes away threads, and takes away half of processes (you can fork+exec, but not fork). Go gives you the net package instead of the socket interface (the latter is not taked away, but it’s really not supposed to be used by the average program). Go gives you net/http, instead of leaving you searching for nginx, or whatever. Life is good when you use pure Go packages and bad when you use cgo.

        The idea is that Go not only has these rich features, but that when you are programming in Go, you don’t have to care about all the OS-level stuff underneath. Go is providing (almost) all abstractions. Go programming is (almost) the same on Windows, OpenBSD and Plan 9. That is why Go programs are generally portable.

        I love this. As a Plan 9 person, you might imagine my constant annoyance with Unix. Go isolates me from that, mostly, and it is great, it’s fantastic.

        But it doesn’t go deep enough.

        A single Go program instance is one operating system running some number of processes (goroutines), but two Go program instances are two operating systems, instead of one distributed operating system, and in my mind that is one too many operating systems.

        “Deploying” a goroutine is one go statement away, but deploying a Go program still requires init scripts, systemds, sshs, puppets, clouds, etc. Deploying a Go program is almost the same as deploying C, or PHP, or whatever. It’s out of scope for the Go operating system. Of course that’s a totally sensible option, it’s just doesn’t align with what I need.

        My understanding about Erlang (which I know little of, so forgive me if I misrepresent it) is that once you have an Erlang node running, starting a remote Erlang process is almost as easy as starting a local Erlang process. I like that. I don’t have to fuck with kubernetes, ansible, it’s just a single, uniform, virtual operating system.

        Goroutines inside a single process have very rich communication methods, Go channels, even mutexes if you desire them. But goroutines in different processes are handicaped. You have to think about how to marshal data and RPC protocols. The difficulty of getting two goroutines in different processes to talk to each other is the about the same as getting some C, or Python code, to talk to Go. Since I only want Go to talk to Go, I don’t think that’s right. It should be easier, and it should feel native. Again, I think Erlang does better here.

        Goroutines have no process ids. This makes total sense if you restrict yourself to a single-process universe, but since I want a multi-process universe, and I want to avoid thinking about systemds and dockers, I want to supervise goroutines from Go. Which means goroutines should have process ids, and I should be able to kill and prioritize them. Erlang does this, of course.

        What I just described in the last two paragraph would preclude shared memory. I’m willing to live with that in order to get network transparency.

        Go programs have ways to debug and profile themselves. Stack traces are one function call away, and there’s a easy to use profiler. But this is not enough. Sometimes you need a debugger. Debugging Go programs is an exercise in frustration. It’s much difficult than debugging C programs.

        I am probably one of the very few people on planet Earth that knows how to profile/debug Go programs with a grown-up tool like DTrace or perf. And that’s because I know assembly programming and the Go runtime very well. This is unacceptable. Some people would hope that something would happen to Go so that it works better with these tools, but frankly, I love the “I am an operating system” aspect of Go, so I would want to use something Go-native. But I want something good.

        This post is getting too long, so I will stop now. Notice I didn’t feel a need for generics in these 9 years. I must also stress out that I am a low-level programmer. I like working in the kernel. I like C and imperating programming. I am not one of those guys that prefers high-level languages (that do not have shared memory), so naturally wants Go to be the same. On the contrary. I found out what I want only through a decade of Go experience. I have never used a language without shared memory before.

        I think Go is the best language for writting command-line applications. Shared memory is very useful in that case, and the flat, invisble goroutines prevent language abuse and “just work”. Lack of debugger, etc, are not important for command-line applications, and command-line applications are run locally, so you don’t need dockers and chefs. But when it comes to distributed systems, I think we could do better.

        In case it’s not clear, I wouldn’t want to change Go, I just want a different language for distributed systems.

        1. 11

          I’ve done some limited erlang programming and it is very much a distributed OS to the point where you are writing a system more than a program. You even start third party code as “applications” from the erlang shell before you can make calls to them. erlang’s fail fast error handling and let supervisors deal with problems is also really fun to use.

          I haven’t used dtrace much either, but I have seen the power, something like that on running go systems would also be neat.

          1. 5

            Another thing that was interesting about erlang is how the standard library heavily revolves around timers and state machines because anything could fail at any point. For example gen_server:call() (the way to call another process implementing the generic service interface) by default has a 5 second timeout that will crash your process.

          2. 2

            Yes, Go is an operating system, almost. Almost, but not quite. Half and operating system. As an operating system it lacks things like memory isolation, process identifiers, and some kind of a distributed existence.

            This flipped a bit in my head:

            Go is CMS, the underlying operating system is VM. That is, Go is an API and a userspace, but doesn’t provide any security or way to access the outside world in and of itself. VM, the hypervisor, does that, and, historically, two different guests on the same hypervisor had to jump through some hoops to talk to each other. In IBM-land, there were virtual cardpunches and virtual cardreaders; these days, we have virtual Ethernet.

            So we could, and perhaps should, have a language and corresponding ecosystem which takes that idea as far as we can, implementation up, and maybe it would look more like Erlang than Go; the point is, it would be focused on the problem of building distributed systems which compile to hypervisor guests with a virtual LAN. Ideally, we’d be able to abstract away the difference between “hypervisor guest” and “separate hardware” and “virtual LAN” and “real LAN” by making programs as insensitive as possible to timing variation.

          3. 18

            How can vgo - announced just two days ago - already be the zeitgeist answer for “all of go’s dependency issues are finally solved forever”?

            govendor, dep, glide - there’s been many efforts and people still create their own bespoke tools to deal with GOPATH, relative imports being broken by forks, and other annoying problems. Go has dependency management problems.

            1. 2

              We will see how it pans out.

            2. 15

              Go has the best backwards compatibility I have seen (I’m pretty sure code from go version 1.0 still works today.).

              A half-decade of code compatibility hardly seems remarkable. I can still compile C code written in the ’80s.

              1. 11

                I have compiled Fortran code from the mid-1970s without changing a line.

                1. 1

                  Can you compile Netscape Communicator from 1998 on a modern Linux system without major hurdles?

                  1. 13

                    You do understand that the major hurdles here are related to the external libraries and processes it interacts with, and that Go does not save you from such hurdles either (other than recommending that you vendor compatible version where possible), I hope.

                  2. 1

                    A valid point not counting cross platform portability and system facilities. Go has a good track record and trajectory but you may be right.

                  3. 5

                    Perfect list (the good things, and the missing things).

                    1. 3

                      The fixes the go team have finally made to GOROOT and GOPATH are great. I’m glad they finally saw the light.

                      But PWD is not a “research concern” that they were putting off in favor of engineering. The go team actively digs their heals in on any choice or concept they don’t publish first, and it’s why in spite of simple engineering (checking PWD and install location first) they argued for years on mailing lists that environment variables (which rob pike supposedly hates, right?) are superior to simple heuristics.

                      Your “good things go has” list is also very opinionated (code manipulation tools better that C# or Java? Distrribution of binaries.. do you just mean static binaries?? Backwards compatibility that requires recompilation???), but I definitely accept that’s your experience, and evidence I have to the contrary would be based on my experiences.

                      1. 4

                        The fixes the go team have finally made to GOROOT and GOPATH are great.

                        You haven’t had to set, and should’t have set GOROOT since Go 1.0, released six years ago.

                        (which rob pike supposedly hates, right?)

                        Where did you get that idea?

                        1. 5

                          Yes, you do have to set GOROOT if you use a go command that is installed in a location different than what it was compiled for, which is dumb considering the go command could just find out where it exists and work from there. See: https://golang.org/doc/go1.9#goroot for the new changes that are sane.

                          And I got that idea from.. rob pike. Plan9 invented a whole new form of mounts just to avoid having a PATH variable.

                          1. 4

                            you do did have to set GOROOT if you use a go command that is installed in a location different than what it was compiled for

                            So don’t do that… But yes, that’s also not required anymore. Also, if you move /usr/include you will find out that gcc won’t find include files anymore… unless you set $CPPFLAGS. Go was hardly unique. Somehow people didn’t think about moving /usr/include, but they did think about moving the Go toolchain.

                            Plan9 invented a whole new form of mounts just to avoid having a PATH variable.

                            No, Plan 9 invented new form of mounts in order to implement a particular kind of distributed computing. One consequence of that is that $path is not needed in rc(1) anymore, though it is still there if you want to use it.

                            In Plan 9 environment variables play a crucial role, for example $objtype selects the toolchain to use and $cputype selects which binaries to run.

                            Claiming that Rob Pike doesn’t like environment variables is asinine.

                            1. 20

                              “So don’t do that…” is the best summary of what I dislike about Go.

                              Ok, apologies for being asinine.

                            2. 1

                              I always compiled my own go toolchain because it takes about 10 seconds on my PC, is two commands (cd src && ./make.bash). Then i could put it wherever I want. I have never used GOROOT in many years of using Go.

                          2. 0

                            C# and Java certainly have great tools, C++ has some ok tools. All in the context of bloated IDE’s that I dislike using (remind me again why compiling C++ code can crash my text editor?). But I will concede the point that perhaps C# refactoring tools are on par.

                            I was never of the opinion GOPATH was objectively bad, it has some good properties and bad ones.

                            Distrribution of binaries.. do you just mean static binaries? Backwards compatibility that requires recompilation???

                            Dynamic libraries have only ever caused me problems. I use operating systems that I compile from source so don’t really see any benefit from them.

                        2. 8

                          Go is the most opinionated language I know and it can be fun if you adhere to them, otherwise things can get a bit hairy (e.g. developing outside the GOPATH). Following the language’s idioms is something you’ve to deal with when developing Go applications.

                          1. 4

                            Developing outside GOPATH is currently worked on: https://research.swtch.com/vgo-intro

                            1. 2

                              This is really nice to hear, currently I use a project-template with some Makefile and shell magic to simulate a custom GOPATH.

                              1. 1

                                Great template! Thanks for sharing. Though, I hope vgo eventually becomes the official go tool and makes this unnecessary :-)

                          2. 7

                            My sense of the error handling issue is that Go implicitly says “We’re not going to let you hide or abstract away your error handling. We’re going to force you to make it explicit so that you really think about it.”

                            When you start to use the chaining style presented in the article as an alternative (in other languages), you can hide a lot with bad names - lead the reader of the code to believe the handling isn’t there. Throwing exceptions puts people in that mode too.

                            1. 11

                              It’s a valid opinion. I don’t share it, though. It seems to be based on:

                              • Politics and people (Rob Pike does not like syntax highlighting and the whole Go team seems to be okay with it)
                              • He doesn’t like the way of importing foreign source code (which I quite like because you just import git repos)
                              • Go error handling. I kinda like, although it’s a bit verbose. There’s a blog post about methods to handle errors differently.
                              1. 4

                                As long as I can get syntax highlighting in my chosen editor, where I spend the vast majority of my time reading Go code, it’s not that big a deal that it’s missing from the playground, where code snippets tend to be sort enough that the benefit of syntax highlighting is marginal at best.

                                As for the fact that Go expressly makes it hard to write complicated code, that does seem to result in Go code being much easier to read, as opposed to write. Given than almost all programmers working on teams will spend the bulk of their time reading other people’s code, as opposed to writing their own, this is totally worth it.

                              2. 5

                                Great post. While I see the points I am surprised that the author is using Go and having a Go job. Choosing Go when not liking these decisions strikes me as a very odd thing to do, since we certainly are in no lack of programming languages.

                                I agree that Go based on this should not be as popular and am sure that if Go wasn’t “backed” by Google it would probably be even less popular than D, because compared to what D offers the philosophies (as in styles of thinking and approaching problems) embraced by Go seem to match way more real life software projects and companies. I know this is a very subjective thing to say, but also looking at all sorts of popular programming languages and in general languages that get developed this sticks out.

                                Having used quite a few languages in real applications, learning them instead of having to use them (and thereby not trying to replicate programming styles of other languages) I have seen how languages starting out with the philosophy of being simple end up being the very opposite as they become popular. While it is natural that software gets more complex for reasons such as portability, performance, edge cases that are found, etc. a lot of it often stems from a shift where the try to become languages for every use case and every programming style. There is nothing wrong about this and there are languages whose main feature is exactly this.

                                However, without a very, very opinionated “leadership” (decision maker, arbiter, etc.) every language will turn into something very similar, mostly having differences in terms of syntax, and whether the community agreed on camel or snake case.

                                I think with Rob Pike and others there is a chance for this not happening with Go, or at least a lot slower than with other languages. However, given that it’s “hot tech by Google” that a lot of people who usually prefer other languages use this is a big challenge.

                                Something that is a good development in my opinion, but something that Go doesn’t really follow is that people start to reuse existing VMs (JVM, Erlang/BEAM, etc.). Maybe this will lead to people being able to choose the language they want to use a lot less based on the size of communities, as more and more code/knowledge can potentially be reused.

                                On a related note I wonder whether HTTP/the web will continue to be one of most essential use cases for any kind of developers. I don’t think this will change in short or mid term. Honestly it wouldn’t surprise me if the corner stones still will be there in 50 years, but if it would change, I think this will have a huge influence on programming languages, as many of them (including Go) at least in some regards are optimized for use cases around web applications. At least trend wise a lot about which programming languages are on the rise or coming close to extinction, or being used for way longer than one would have imagined because of how the web developed.

                                While people might have imagined that C and C++ would still be around 20 years ago, I don’t think many predicted, that PHP will still be around and used by quite a lot major websites on such a huge scale or that JavaScript would be one of the most requested languages, and that there are quite some job offers with more than double the salaries of a senior C++ developer Cray Inc.

                                But going off topic here.

                                tl;dr: I think that the author wants to have another job using another programming language, better suiting his personal preferences, which of course are valid.

                                1. 5

                                  I can’t speak for Go’s genesis within Google, but outside of Google, this underanalysed political stance dividing programmers into “trustworthy” and “not” underlies many arguments about the language.”

                                  Correct, but not unique. Java is probably as popular as it is precisely because encapsulation is so strong, allowing you to protect your code from others who you worry will do harm.

                                  On the unskilled programmers side, the language forbids features considered “too advanced.”… This is the world in which Go programmers live - one which is, if anything, even more constrained than Java 1.4 was.

                                  My feelings on Go is that it’s largely defined by what isn’t in there than what is. What isn’t in there allows me to read the code in the stdlib and understand it. I can look at any piece of code from another developer and not worry that there are multiple abstraction levels that I’m missing that alter the behavior. There isn’t enough in the language to support subset dialects like you’ll see with something like Perl or even Java (a Spring code base looks very different to a POJO code base looks very different to someone obsessed with interfaces looks very different to…)

                                  I think Go succeeds very admirably along the dimension of “everyone at the company should be able to read everyone else’s code and modify it safely.” That necessarily means some blunting of the scissors. If you want the bleeding edge tool, Rust is a great alternative, but there’s enough in it that I would be afraid letting a junior programmer at it without guidance, and that new people to Rust will have a long ramp up time to figure out how the code base works and be able to modify it.

                                  EDIT: This is not to say I don’t have my own axes to grind: I do not like not being able to easily differentiate between types of errors in particular. I don’t feel like interface{} is a good solution to anything, but I don’t necessarily have any desire for generics either. I don’t care for the way contexts work, although I understand why they exist (I like this proposal about this).

                                  1. 3

                                    Meta: Should be tagged rant. Users with 10 karma can hit “suggest” under the story and add tags.

                                    1. 3

                                      I love GOPATH — I actually adopted the idea for everything, my GOPATH is ~/src and that’s where I store all software repos. I use ghq to clone non-Go repos into the right directory. I even set up vim-plug to use these paths.

                                      I like the cross-compilation story (made possible by not using libc at all). (Fun fact, Go currently supports FreeBSD ARMv6/7 but not AArch64. Rust supports FreeBSD AArch64 but not ARMv6/7. )

                                      I dislike the hostility to developer ergonomics. if err != nil is shameful.

                                      I hate the Plan9-based Go assembler. It’s objectively a horrible disaster. That’s one thing AT&T and Intel syntax fans would agree on ;)

                                      Look at the hacks people do to make their C/asm functions work without cgo overhead. I failed and used cgo. Sounds like the deepest internals of Go are based on Plan 9 fanboyism rather than solid pragmatic engineering…

                                      1. 3

                                        I love GOPATH — I actually adopted the idea for everything, my GOPATH is ~/src and that’s where I store all software repos.

                                        Can’t agree enough.

                                        I like the cross-compilation story

                                        I hate the Plan9-based Go assembler.

                                        These things don’t go together. It might be difficult to see at first, but one reason why Go succeeds at the former, is because of the later.

                                        made possible by not using libc at all

                                        That’s not it. Using libc has nothing to do with it. It’s the same even if the port uses libc, look at the Windows or Solaris ports (I wrote the Solaris one). It uses libc, but it uses libc without the user-visible part of cgo. And cross-compilation works just fine. This is made possible only by the custom Plan 9-inspired toolchain.

                                        1. 5

                                          The Go toolchain is not unique in that. LLVM natively cross-compiles to anything too. LLD cross-links.

                                          I just tested this. Built a 32-bit Linux binary on 64-bit FreeBSD and ran it (the file is this code):

                                          $ cc -fuse-ld=lld -nostdlib -target i386-unknown-linux-none -o nostd nostd.c
                                          $ brandelf -t Linux nostd
                                          $ ./nostd

                                          Not using libc has everything to do with not needing to download the target’s native libraries to link to them!

                                          1. 3

                                            Not using libc has everything to do with not needing to download the target’s native libraries to link to them!

                                            As I said… You can cross-compile Go binaries to Solaris, and get binaries which use libc.so (unlike your example), and most certainly it does not need to download Solaris libc.so libraries to do it. Same with Windows. This is something you can’t do with any other toolchain.

                                      2. 1

                                        And I do not like that there are no timestamps on posts here (without following through to bitbucket)–the reason? Some of the complaints are being addressed as we speak, and it’d be nice to have that context. The general sentiment, however, I agree with, but I like that the position of the article was I not “you should.”

                                        1. 0

                                          I really like Go, for these reasons: https://getstream.io/blog/switched-python-go/

                                          I don’t think any other language comes close for this use case:

                                          • the task i’m working on is performance intensive
                                          • it doesn’t require me to use CGO (go is not great in this area)
                                          • regex and json parsing performance isn’t super important (java & c++ still do this better)
                                          • development time is important (Java and C++ have their advantages for some things, but it takes more time to build things)

                                          Also, I like how every release of Go improves something i care about. For instance 1.10 substantially reduced the time it takes for code to compile. 1.9 shaved a few milliseconds of our ranked feeds. It’s awesome that stuff you don’t work on just improves as time goes by.