- Several months ago while doing engineering work at a startup, I ran into a problem that demanded an efficient backend that could scale up/out/etc. The existing implementation was part of a Rails app, and we couldn’t reasonably grow within that framework. The company had almost no non-Rails code, so a larger question arose about which language we should use.
- While at Google, I spent the better part of a decade building distributed systems from scratch. Those who’ve worked at Google would probably agree that libraries and systems slow development down more than languages per se, but – that said – most of the actual code I or my teams wrote was in C++, typically with bits and pieces of Java and python around the edges. Since Google, I have been [indecently?] exposed to the contemporary menagerie of dynamic languages (ruby, javascript, etc). Somehow, though, none of the above felt right for this new project.
- C++: too much rope, hard to maintain, painful to introduce at a company with no prior C++ footprint, frightens junior devs who no longer absorb relevant memory management idioms in school
- Java: too verbose, too many FactoryFactories, painful to tune
- Python/Ruby/Javascript: my experience is that large systems are difficult to maintain in these languages, as the dynamic typing makes it difficult to refactor large codebases without introducing errors in poorly tested areas of a repository
- Rails in particular: Independent of ruby, I see Rails as the emperor with no clothes on. A subject for another post, but I will try my damndest to steer clear of it in the future.
- At this point, Go became a viable contender. However, when I was doing my initial research about it, I had trouble finding high-level impressions from recent converts that (a) sounded like they were written by an author with substantial experiences building systems in other languages, and (b) didn’t seem zealous. This post is my attempt to provide the sort of overview I would have valued reading before I started my own investigation into the language.
- At the time of this writing, the subsystem I described at the beginning of this post has been written (in Go) and running for many months without issue. It took about two weeks to learn the language, write the necessary code, and deploy it in the startup’s production environment on EC2. The short story is that I’ve been mostly delighted so far. What follows are my impressions.
- Good things:
- Go feels like a “small" language. It seemed as though I’d absorbed the core language and builtins – with the exception of the reflection APIs, which I haven’t had occasion to use yet – in about a week. (I of course have plenty to learn about the standard libraries that build on that foundation, but that’s to be expected) Contrast this with contemporary C++, Java, or Scala, all of which have plentiful innate complexity.
- Goroutines and channels: where have you been all my life? I’ve never had so much fun or spent so little time reading about edge cases while implementing a thread-safe service that uses multiple cores, allows for multiple concurrent readers while excluding writers, etc, etc. Of course we all know how to build these things in other languages, but it’s such a joy that they’re made simple in Go.
- Go Interfaces: where have you been all my life? This is the type system I always wanted… it feels a little bit like using python/ruby/javascript/etc in that Go interfaces resemble duck typing, except that in this case there’s a spec for the duck. This helps from a readability/maintainability standpoint, of course, but it also means that the compiler can tell you if you’ve screwed up and fat-fingered a variable name somewhere (rather than finding out at runtime, as is the case with many popular dynamic languages (yes, I know, linters can help here — but the Go compiler is better at it and can’t be forgotten or avoided by lazy team members!)).
- `go fmt` ends absurd style debates once and for all. There is a single sanctioned Go style, and `go fmt` (which integrates easily into the editor of your choice) enforces it on any legal go source file. I also appreciate how the formatting tool borrows from the “elastic tabstops" demo (http://nickgravgaard.com/elastictabstops/), though that’s a minor point.
- The go toolchain encourages you to organize source files in a sensible manner, as well as external (third-party) dependencies, etc. I wish it didn’t rely on environment variables for critical paths, but that can be hidden via scripts/etc.
- Compiling down to a single statically-linked binary makes me feel safe and secure, like I’m back in the warm embrace of Google’s internal systems. It also means that the “deployment yak" is several steps closer… (esp compared with deploying modern Java applications, which is a minor nightmare IMO)
- For what it’s worth, my new Go service is fast (fast enough that I have a huge amount of headroom, anyway)… Obviously this undocumented assertion of mine is not equivalent to reporting benchmark results, but suffice it to say that Go passed the sniff test with flying colors (and absolutely blew the doors off of a previous ruby implementation of an analogous service, though that is hardly grounds for bragging!).
- Bad things:
- The standard library is missing some things I consider essential. I’m thinking in particular about some manner of sorted associative container. There are several open-source projects that provide mature implementations of RBTrees, etc, but they’re missing something important… see the next point:
- It’s not true that Go doesn’t have generic types: it does, you just can’t write them yourself. This omission has been beaten to death on the Go mailing list, but it still drives me nuts, especially since there are some core container data types that are missing, leaving the user casting and recasting and recasting as objects enter and leave containers.
- Especially while I was in the early learning stages, I was disappointed with the amount of troubleshooting content on the web (StackOverflow threads, mailing lists, etc).
- There are a few odds and ends that don’t quite fit right: the `make` builtin feels like a hack, especially since it doesn’t apply to user-defined types; the rules about when you have to derefence pointers and when the language does it for you are still hazy around the edge cases; I think the logging library is too restrictive; and probably other details I’m forgetting. C’est la vie.
- Anyway, the net is that I’m presently starting my own company and I plan to use Go for all backend services (the main frontend is – surprise, surprise – mobile only). It’s not perfect, of course, but I’ve had more fun writing code in Go than I have in any other language that I’ve come across: if you can afford to introduce a new language into your own project or company, give it a shot and let the world know what you think!
Stikked
