It’s been 418 days since my first Github commit of Go code. In that time I’ve written a Syslog-to-Kafka producer, a Raft-based distributed SQLite database, a near real-time log search system, and become a core developer of InfluxDB.
So if there is one word to summarize Go, it’s productive.
No IDE required
I’ve written much of my Go code using nothing but vim. Sometimes Sublime, but often just vim. I once tried to write Java code using vim, and the runaway proliferation of source files, of AbstractSingletonProxyFactoryBeans, quickly made an IDE required. Not so with Go, and this is important.
Fast build times
Compile and link a C++ program. Do it 10s of times a day. It adds up to minutes, sometimes hours. But not with Go. The entire InfluxDB system compiles, with Go 1.4, in 3 seconds on my machine. An entire distributed time-series database compiled to native code in 3 seconds. This has done wonders for the productivity of the InfluxDB development team.
The standard library is a joy to work with. Many parts of it show me that the Go team really did have systems software in mind when designing the language. Little things like making it easy to set read-and-write timeouts on sockets, or gather server statistics, have made it so much faster to produce robust and reliable code.
Key data structures built-in
Maps are a ubiquitous data structure in programming — I’d guess that 10-20% of all Go code I work with involves maps. That the Go team decided to make them a built-in for a statically-typed language was an incredibly important decision, that has made the language so much more powerful.
By building both in, it has resulted in a standard and systematic way for Go programmers to deal with these data structures. This has become a force-multiplier within teams of Go developers and the wider Go community.
Sometimes you don’t realise how much time you are spending doing something until you are not doing it any longer. Source code layout conventions are one of those things. Debating bracket placement, character spacing, noisy diffs due to whitespace changes — all banished to a time before Go. go fmt is an inspired tool, and it is a tribute to the Go team that the choices made by the tool have been so readily accepted by the Go community.
Even though this is widely acknowledged when discussing Go, I believe it remains an under-appreciated attribute of Go, something that will become even more important as time passes. Network and storage are cheap, but the deployment, configuration, and management of modern computer systems is not. Anyone who has spent time in Technical Operations — as I have — knows the pain of dependency hell, JDK upgrades, or installing Perl-bindings. The single-binary approach is, and will remain, a significant productivity boost.
The test framework is excellent. And perhaps as important, using just the standard library’s test framework has proved to me that using only the standard library (when humanly possible) is the right approach to writing Go code. There are many, many test frameworks for Go, and the temptation to use a third party library is strong, but sticking with one framework keeps the code consistent, which helps productivity enormously.
The performance analysis tools — CPU profiling and memory profiling — have been key to development at InfluxDB. After all, it’s only with databases that O(n) comes alive. It’s been fascinating analysing — and fixing — the database Go code I’ve written using the CPU profiler.
Tell me how you really feel
So is there anything not so bright-and-shiny?
Strangely, some key parts of Go don’t feel like they have made a huge difference. Sure garbage collection is nice, but I never had too much trouble writing leak-free C and C++, not with technologies like Valgrind and Boost Smart Pointers. Now I may underestimate the work GC is doing for me — quite possibly — but if I had to track the memory myself, and still write Go code, I don’t think I’d really mind.
I certainly find myself writing chunks of boilerplate during Go development. This can make code maintenance more difficult. With a language like C++ there is a drive to abstract everything to avoid this issue, and to make heavy use of features such as templates (generics). But anyone who has written a reasonably-sized C++ program knows that this drive is relentless and that’s the problem — it’s very easy for abstraction to go too far.
Within our industry there is now a drive to return to simplicity. Go’s easy acceptance of writing a little boilerplate to make source code navigation less cumbersome , and its comprehension easier, is actually refreshing.
GOPATH is weird. Once you get the hang of it, it’s fine, but early in my Go programming days it caused plenty of confusion. It feels like a bolt-on, and I’ve learned to use a separate GOPATH for every project. It doesn’t feel right, and often I wonder how it made it into the Go system.
The Go Way
Go feels like a pedantic language, but that suits most programmers. It certainly suits me.