Last week, I had the pleasure of attending Dropbox's
annual company hack fest. It
was a great opportunity to get a look at how Dropbox works internally, and
mingle with the smart and driven folks who make one of my favourite products.
In the spirit of hack week, me and my friend
@alexdong decided to do our project in Go. We'd
both wanted to explore the language, but had never quite been able to make time
- a week-long code holiday seemed to be the perfect opportunity. I was hopeful
that Go would turn out to hit a magical sweet spot: a light set of abstractions
hugging close to the machine, while still providing the indoor plumbing and
civilized conveniences of life that I had grown used to with languages like
Python. Five days of furious hacking later, I can report that Go might well
deliver on this promise, but has enough annoying personality quirks that I will
think twice about basing any more projects on it.

My main beef with Go has nothing to do with fundamental language design, and
may seem almost inconsequential at first glance. The Go compiler treats unused
module imports and declared variables as compile errors. This is great in
theory and is something you might well want to enforce before code can be
committed, but during the actual process of producing code it's nothing but
an irksome, unnecessary pain in the ass. Let's look at a concrete example,
starting with a snippet of code as follows 1

I'm a firm believer that printing stuff to screen is a programmer's best
debugging tool, so say we're hacking away and want to print the value of m
while running our unit tests. We change the code as follows, adding an import
for the "fmt" module and a call to Print:

A few seconds later, we want to re-enable the Print statement - so up we go
again to the top of the module to re-enable the import. This is even worse when
we want to, say, comment out the DoSomething call while hacking:

This is also a compile error because now m is unused. We have to hunt up in
our code to find the declaration, which could be explicit or implicit using an
:= assignment. So, in this case we find the declaration, and use the magic
underscore name to throw the offending value away:

That should fix it, right? Well, no. It turns out we've previously declared and
used err (a very common idiom), so this is still a compile error. We're
using the "declare and assign" syntax, but have no new variables on the
left-hand side of the ":=". So we need to make another tweak:

Five seconds later, we want to re-enable DoSomething, and now we have to
unwind the entire process.

The cumulative effect of all this is like trying to write code while someone
next to you randomly knocks your hands off the keyboard every few seconds.
It's a pointlessly pedantic approach that adds constant friction to your
write-compile-test cycle, breaks your flow, and just generally makes life a
little harder for very little benefit. There's no way to turn this mis-feature
off, no flag we can pass to the compiler to temporarily make this a warning
rather than an error while hacking2.

The irony of the situation is that I agree with the sentiment behind this. I
don't want dangling variables or imports in my codebase. And I agree that if
something is worth warning about it's worth making it an error. The mistake is
to confuse the state we want at the conclusion of a unit of hacking3, with
what we need at every point in between, during the write-compile-test cycle.
This cycle is the core of the process of actually producing code, and the
exhilarating sense of weightlessness that you get when
hacking in Python is largely due to the fact that the language works really,
really hard to optimize this process. Go has given away this feeling of
exhilaration, basically for nothing.

Despite all this, it's still possible that the benefits of Go do outweigh its
irritating personality. Interfaces, memory management, first-class concurrency
and static type checking is a knockout combination, and the language in general
has something of the taut practicality that I love in C. So, despite the
rantiness of this post, I'll keep hacking on our project and make sure I
produce a few thousand more lines of code before making a final call on the
language. Look for a project release and a blog post along these lines in the
coming months.

I edited this paragraph a bit for tone. I originally accused the Go
documentation of being faintly smug about all of this - which is not fair, and
doesn't add anything to the argument. ↩

Why don't we have a word for this? By "unit of hacking", I mean the work
that goes on between starting to hack on a change-set and doing a commit. At the
beginning and at the end, the code is in a clean state, but in between there
are many periods of transition where cleanliness requirements are relaxed. ↩