Traditionally I’m a pretty late adopter of new trends, and Go has been out for something like 6 years now, so I figured it would be a good time to try it. I’ve been learning it for about 2 months; I’ve done dozens of Codility exercises, written a WebSocket server and a statusbar daemon, so I think I’ve given it a good go. My experience has been generally positive and I’ll definitely use it in future.
The Go FAQ is pretty clear about the objectives for the language: it’s supposed to be easy to program in, but also efficient and (type) safe. It’s obvious from spending some time with the language that it’s met these goals. Make no mistake, Go is not a low level language, although it produces similar effects. It has a simple, lightweight type system, built-in support for concurrency and garbage collection. You can even import packages straight from GitHub. It might be a compiled language with C-like syntax, but it’s easily accessible to those with little or no experience with C or C++.
The language’s syntax has intentionally been kept simple and clean, and while I feel that this does limit expressiveness (more on this later), it undoubtedly has advantages in making the language easy to learn. Generally I’m happy with the features that Go does include, with one exception: I strongly dislike the error handling.
Go takes the approach to allow functions to return multiple values, and the convention is to return an error as one of the return values. There’s an error
type that makes it easy to handle these kind of errors in a consistent way. But the reason I dislike errors as return values is that error checking should not be optional. In my opinion, if it’s possible to call your function without checking the error, you’re asking for trouble. Of course, you can argue that you/your employees are disciplined enough to check every error, but why take the chance? The fact that errcheck – errcheck checks that you checked errors even exists is a sign of a language flaw, in my opinion.
I much prefer try/catch
and the like for handling errors across function/package boundaries. Go does actually have panic/recover
to provide a similar mechanism, but it generally seems less preferable to returning error values. They explain their case here, but I still strongly disagree. Ah well.
Go’s syntax makes it easy for developers to pick up, and the lack of complexity means that once you’ve grasped the syntax, it’s not too difficult to understand other Go code. This is definitely good from a developer’s perspective. The toolchain is intuitive and easy to use; I don’t feel like I need an IDE to set up or build my programs. The compiler is strict but not annoyingly so, and fixing the compile errors feels quite satisfying: your program probably works afterwards. Generally, it’s a language that doesn’t get in your way, and it’s not difficult to focus on writing code.
However, the language trades simplicity for expressiveness, and I often lament that I could have written the same thing in far less code in Ruby. The benefit is that my programs are compiled (finally I can write programs that load quickly!) and type safe. They also use less memory and are faster at runtime, but I think this point is overrated. It takes a very specific class of problems to benefit from faster runtime performance; if your code is heavily IO/database bound or relies on a 3rd party API, the speedup from a compiled program isn’t going to help much. I think slow runtime code should not be the primary reason to use a different language, and should be solved by proper code optimization/architecture instead.
Still, there are definitely problems that Go works very well for. For example, web services where low latency is critical (especially those that are CPU intensive) are an excellent use case. Command-line programs where fast startup time is important. Background jobs that are CPU-bound can be shipped off to Go workers for faster processing. These tasks make Go an excellent tool to have. I just wouldn’t use it for a traditional website, where the bottleneck is almost always the database.
For large teams, Go’s simplicity and ease of adoption is extremely attractive. It enforces some code quality rules at compile time (eg. removing unused variables), and code style via other tools (eg. go fmt
, go vet
and golint), and is of course type safe. There is a well-defined, enforceable, ‘right’ way to do a lot of things in Go, and I think this definitely helps with maintainability. I just wonder if this takes some of the fun out of it, though. One of the main reasons I enjoy programming (and loved Perl for many years) is that I can be creative and write elegant solutions that read well in the context of the problem. Code efficiency is not the only factor; programmer productivity is, too, and Go feels like it was primarily designed to solve computer problems, not human ones.
Then again, I’m not planning to give up Ruby any time soon, and there’s definitely room for Go in my toolbox. And Go is still young; Perl had been out for 15 years before I started learning it. So it’ll be interesting to see how it develops.
-Mike