Man, Go is damn nice to work with. I figured I would hold off writing about it until after I had used it seriously at work for a couple weeks. We wrote a couple projects in Go that never really got off the ground, but the one I have been spending most of my time on nowadays is going the full 400 meters to production.
First, the good. Structure and organization are key for me when developing.
If the structure of the code isn’t uniform, it makes it immensely difficult
to work with. This is one reason I get really angry with Node – there’s no
uniformity at all. gofmt
helps ease the tension between colleagues and
makes an easy case to follow “the Go way.” There are still tensions when it
comes to package organization but those arguments are fewer now. So in
terms of code cleanliness and organization, my inner OCD gives Go a big
thumbs up. And my inner peacemaker is grateful, too.
The Go community is a really vibrant one. If you log onto the #go-nuts
channel on IRC (Freenode), you’ll find lively conversation at all hours of
day. The golang-nuts mailing list is also a great place to ask questions
and to passively learn more about the language and its application in
various problem spaces. There are loads of talks by the Go Team at Google
as well; I would recommend talks by Rob Pike and Brad Fitzpatrick if you’re
interested in the technical deep-dives.
I come from a background of PHP, Ruby, and JavaScript, where types feel
like an afterthought. Working with Go’s strict typing system has been a
really nice ease into the world of strict types. I tried my hand at Rust a
little while ago and got so frustrated by the type system that I ended up
putting it down. Go’s type system feels like one that helps you out rather
than holds you back. It doesn’t have generics, but I barely remember
generics from my Java days (oh, high school) so I don’t miss them much.
Generally, I find a way for interfaces to satisfy that need if there is the
need for generics. At any rate, Go’s type system gives me confidence that
there won’t be some silly type error at runtime (as long as I stay away
from the dreaded interface{}
non-type).
Working with a compiled language is also a really nice change. I can build
a binary with go build
and ship that out to my production servers without
even blinking. Hell, using scp
or rsync
to deploy?! You have those
ansible/capistrano folks weeping in their sleep. The compiler’s type
checking also really helps. If it builds, it probably runs – though it
isn’t guaranteed that you hooked up all the bits correctly even if it does
compile. A file of empty methods is still valid in Go. At any rate, working
with the compiler has been a marvelous change coming from Ruby and
JavaScript where almost every error is a runtime error.
It’s hella fast. A sizable test suite will often run in less than 30 seconds. The Rubyist in me was off hiding in the corner, hoping I wouldn’t notice that a similarly-sized test suite in Ruby would take 3 or 4 minutes to run. Beyond tests, there has been so much optimization in the compiler to make Go as fast as humanly possible – obviously in Google’s best interests because at their scale, every CPU millisecond counts.
Channels. Holy hell, channels are cool. Communicating between goroutines using channels instead of shared memory has changed my world for the better. If you don’t know what channels are, here’s a gentle introduction on Go By Example.
I suppose there is more I could say here, but I imagine you’re satisfied
I really, really miss Enumerable
. There’s no reduce
, no map
– none
of that. Just for
and a growable array type called a slice
. I was
writing Ruby last night and loved how concisely I could operate over large
arrays. I suppose concision and speed are at war here, so at this point I’m
not too broken up about it. For every step made easier by Ruby, there’s a
200ms overhead it seems.
Your code won’t compile unless it’s clean. Evan Miller famously wrote about this in Four Days of Go, where he, amongst other things both good and bad, expresses his frustration that his code must be bereft of unused imports and variables before it will compile. This makes just testing a quick fix a bit slower than usual. Given Google’s cost structure, this makes sense: an extra few minutes a developer spends cleaning up the code saves a lot of money on CPU time later. This doesn’t tend to frustrate me as much having some erroneous import or variable which used to plague me when I was learning C. So all in all, it just slows me down a bit.
Go’s still a little young and the idea that you shouldn’t make breaking
changes in your library has not spread throughout the community a ton.
go get
simply fetches master
of the repo. Without using some
third-party package manager (like my fave gpm),
you’re stuck using master
of everything. If someone breaks something,
you’re spending your time debugging your integration with this bozo’s code
instead of doing something interesting. That said, it’s nice that the
“don’t break things” mantra is so strictly enforced as a byproduct of using
other libraries. We should stop breaking software so much.
My last few weeks writing Go every day have been marvelous. Our code is of better quality, it’s more consistent, it’s faster, and we’re having fewer arguments about dumb shit like whether to use single quotes or double quotes. Go gives you one way to do things – when working in an unpredictable, dynamic team environment, that’s a huge win.