Hacker News new | ask | show | jobs
by loosescrews 1259 days ago
I'm a Go fan and the author makes some good points. That said, their arguments seem a bit one sided and there are a number of areas where they get close to, but miss what I feel should be the real conclusion.

I'm going to focus on toolchain related issue because I feel better able to speak on these topics. The language issues the author brings up are similar though.

RPCs instead of FFI. There is some truth here. I personally find wrapping foreign code in a C interface and using CGO to be pretty easy, but RPCs are a good choice as well. The author then jumps to this necessarily including TCP overhead which is wrong. If a FFI is the alternative, then running on a single machine is acceptable and a Unix socket is an option (I'm sure there is something similar for Windows).

I have often combined these strategies where I wrap some C++ code in a C library and integrate it into a CGO gRPC service which serves on a Unix socket. I do this because I find setting up an RPC service in Go to be much easier than C++ and I want to isolate the C/CGO.

Using an RPC interface also limits the unsafe code to a process which can be optionally further sandboxed (e.g. with SECCOMP). If you are concerned about memory safety, this might be a good idea even if you are using two languages with better FFIs. Even if both languages are memory safe, the FFI almost never is. In addition to the security benefits, this also makes debugging memory issues easier as it limits their scope.

Another benefit to FFIs being bad is that it has pushed Go to be one of the only languages where most code has no C dependencies. Nearly the entire standard library is pure Go and the parts that aren't are not commonly used or provide optional functionality which is not commonly needed. Most third party libraries I find on GitHub are pure Go as well and only have pure Go dependencies. The majority of Go binaries I have encountered either are or can be built with CGO disabled.

This has two major benefits.

First, it makes Go code much safer. C dependencies are a major source of memory unsafety bugs. The most common C dependency (in Linux land anyway) is glibc. glibc is well known for its terrible code quality and history of security vulnerabilities. Go is one of the only languages which makes it easy to not depend on it. Since the author brings up Rust, I would personally feel better about untrusted data being processed by a pure Go program than a Rust program with C dependencies.

Second, even the best FFIs make code harder to read. If nothing else, now you need to be familiar with both languages. Go libraries usually being pure Go makes it easy to jump into random library code and figure out what is going on.

The author also mentions build systems with a vague comment about cases not considered important by the Go authors. In my opinion, the true build system for Go is Bazel. Bazel has great Go support because Go was built with Bazel in mind. The fact that Go and Bazel handle imports in almost the same way and that Bazel BUILD files can be generated from Go code is not an accident. Bazel is very powerful and is usually a good fit when you start running into the limitations of the built-in Go build system.