Thoughts after using Go on a few projects.
We have been Wading into Go on some projects recently. In fact we have been using it on small and throw away projects for a while. We first used Go in anger to manage transfering and updating ~500,000 unique files (~1TB total) from an EBS volume to S3.
It was my first code in Go so it isn’t pretty. I am also not sure what the likelihood of it working now are as it used a fork of goamz. The fork was absorbed into central (hah) fork of goamz, but YMMV. The take away is that Go did made dealing with a massive number of files during a large scale migration practicable and I would definitely choose it again.
During that same project migrated a large number of vanity URL redirects. As part of the move there was a rule; if a redirect hasn’t been reviewed in more than 2 years, get rid of it. We had no way to know when rules had last been reviewed. They were stored as apache rules across a dozen servers. So the order was given that redirects had to have a “last reviewed” date. We used Go again to build an in memory redirect server with custom “Last reviewed” headers.
Most recently we have been using Go to write the backend API for an app powered by angularjs on the client side. This our first project which leverages GAE and is expected to have sufficient complexity and lifespan to warrant first class testing. The rest of this post discusses what warts we’ve seen when we got up close and how we have worked around them.
Testing
Testing with Go is a pleasant experience. Go’s standard library ships with a testing package that should feel familiar to most programmers. It is admittedly missing some convenience items like assertions, but that does not have much impact. Many coming from dynamic languages might think this is an ugly feature. However, it is easy enough to include a few of your own.
I have been including the three following functions for testing. I stole them from@benbjohnson’s article. Well really from his Github repo. The only change I made was to make equals use “want”/”got” instead of “act”/”exp” and to change the argument signature and logging order to match Go’s conventions. Here are those three functions:
assert, equals, and ok.assert fails with msg if condition is false.func assert(t testing.TB, condition bool, msg string, v ...interface{}) {
if !condition {
_, file, line, _ := runtime.Caller(1)
fmt.Printf(" 33[31m%s:%d: "+msg+" 33[39mnn", append([]interface{}{filepath.Base(file), line}, v...)...)
tb.FailNow()
}
}
ok fails if an err is not nil.func ok(t testing.TB, err error) {
if err != nil {
_, file, line, _ := runtime.Caller(1)
fmt.Printf(" 33[31m%s:%d: unexpected error: %s