Underutilized and Overutilized Features of Go
Go has become a fascinating lightning rod for software developers. Go has an opinionated design, and as a result, is very polarizing, particularly given Go's recent surge in adoption. Two common themes emerge:
- Go is simple, sparse and uncomplicated. It is limited in certain regards but simple to pick up. It has some nice tooling and makes concurrency approachable.
- Go ignores a number of well-understood advances in programming language design and as a result, locks the developer into the past. The tooling and concurrency features are already common elsewhere.
Both are right, and they imply each other. If Go's design wasn't driven by simplicity, it would have died on the vine as just-another middleware language. On the other hand, you will periodically be frustrated by some of its limitations.
That said, I think most of the criticism of Go results from a tendency for developers to overlook some of its neat features and instead presume that Go only delivers advances for concurrency. I'd like to highlight what I feel are some underutilized and overutilized features of Go.
Underutilized Feature #1: Interfaces
Interfaces in Go allow you to encode a set of required behaviors. In Java, interfaces are a fundamental component of the standard library. In Go, interfaces seem to be less obvious as a design artifact; I tend to see meaningful application of interfaces only in the standard libraries and rarely in third-party contributions. This is a shame!
My current thinking is that any public API you intend to properly support should be developed with interfaces as a key consideration. The primary benefit of publishing APIs that accept interface types as parameters versus concrete types is that this approach allows you to focus on the functionality and behaviors a particular API demands, without making assumptions or causing side-effects through fields in concrete types that may not have specific relevance. Interfaces support composition, so we can decompose and rationalize how our systems are described.
Underutilized Feature #2: IO.Pipe
IO.Pipe is not going to be something you use every day, but it is incredibly powerful if you can wrap your head around an appropriate use-case. IO.Pipe provides an efficient method for moving data around that avoids making unnecessary copies. Consider this gist, which illustrates how to stream words from a dictionary file through a gzip pipe and out to the caller.
Modeling with data is an essential element of good design. Using an optimization like IO.Pipe to represent state transitions is key to bringing about an effective implementation.
Underutilized Feature #3: httptest
The httptest library is an essential tool for testing web services and helps address a nagging problem that has plagued similar projects for years: how to unit test a running service.
Underutilized Feature #4: grpc
grpc isn't really a part of the proper Go ecosystem, but it is clear that Google has made strong Go integration a top priority for the project. Simply put, grpc generates code to enable the service definitions in Protocol Buffers. Go already provides very good support for creating network services, but the additional requirement of Protocol Buffer definitions may be a useful formalization in many scenarios, particularly services that are intended to be durable over a long period of time. Modeling your system with Protocol Buffers provides the added benefit of committing to a data-first development methodology that provides a basis for both code generation and system documentation.
Overutilized Feature #1: Concurrency
We've all been guilty of this. Go's concurrency support is very approachable. It is hard to resist the urge to add in a goroutine or a channel simply because we can.
last update 2015-11-16