After using Go for a couple years, I’ve really come to appreciate its simplicity. I started writing Go at work a couple months ago, and have found it really easy to iterate on – much more so than Python and Java.

As the Go community celebrated its 10th birthday I was thinking about what makes Go unique. I think a lot of the real power of Go has come from the philosophy of its designers: There’s a strong emphasis on forward compatibility, the language doesn’t have flashy features that hurt readability, and it comes out-of-the-box with everything you truly need (a basic test library, solid networking and synchronization primitives, and templating, to name a few).

Forward Compatibility and Dependencies

In my opinion, of recent languages, Go has the sanest versioning and dependency story. I haven’t had a single breakage due to an update in Go. That is significant. Over a similar time period, I’ve had projects break due to Rust’s APIs changing, the infamous Python 2-to-3 transition, and, to be frank, I’m surprised if any of my old JS projects after an npm upgrade.

With Go, I’ve noticed that libraries tend to be stable – almost worryingly so. In Python- and JavaScript-land, if you see a library that hasn’t been updated in 3 years, that’s a sign of death. In the Go ecosystem, you’re probably OK to use it. What is seen as stagnation or neglect in other communities is often a sign of resilience in Go.

I’ve also noticed that in Go I need fewer dependencies, and my dependencies themselves have fewer dependencies. Go doesn’t have a culture of exporting as-much-logic-as-possible to external dependencies. Code duplication is more acceptable in the Go community than elsewhere. This can be frustrating. Sometimes, you just want a good library that performs some type of sanitation or parsing. Many times, you’ll need to write that functionality yourself, or copy/paste it from a StackOverflow answer. Generally I think this is a positive. Fewer dependencies means fewer things that break while you let a project sit idle for a couple months.

Now, this might be a bit unfair to other language communities. Rust hasn’t reached the level of stability that Go has, likely because it isn’t as mature a language, but it’s trending in that direction. Python 3 itself has been forward compatible, and I’d hope that the Python core devs learned their lesson going forward about hard breakages. I don’t have much hope for Javascript in this regard but… uh… *something* *something* WebAssembly and ES10 will fix all our problems.

Alas, Go’s dependency system is not without drama. For a while, the fracturing of dep and glide was really annoying; and, while Go Modules are great, the community still hasn’t reached 100% adoption. Before Go Modules, the requirement to keep everything under your $GOPATH was so annoying that I held off diving into Go for years. Things are better now and show signs of improvement.

As far as forward compatibility, any Go code written since Go 1 was released is guaranteed to work until Go 2, at some indefinite point in the future. That’s a strong commitment that, at least so far, has been really positive to my use of the language.

“You Ain’t Gonna Need It” (But you might want it)

After using Go for a while, I reached a point where I wanted some extra features that the language simply doesn’t support. Generics was the big one for me, as I think it is for many others. Look, I’m repulsed by C++ templates as much as the next person, but having basic collection class generics isn’t that much to ask, right?

The only two generic (ish) data structures that you get out of the box are arrays (slices) and dictionaries (maps). Want to write your own data structures? Well, you’ll either have to make them specific to certain types, or you’ll be staring at a field full of interface{}. There’s a time and a place for both approaches, but… sometimes I wish I could just import a type-safe generic bidirectional map, for example.

Go is “You Ain’t Gonna Need It” taken to the extreme. Need is the operative word here. You won’t need generics, but you will almost assuredly want them. Ditto syntactic sugar for error handling, functional programming, and operator overloading.

As the cliché goes, sometimes less is more. With the exception of named return values, I can’t think of any footguns that have been added to the language. If nothing else, its impressive how much restraint the Go core developers have shown against the swarms of developers asking for, say, generics in Go. The hazy future of Go 2 is really exciting to me, as I’m looking forward to seeing what an idiomatic “next generation” Go will look like.

Just Enough Batteries Included

For the use cases where Go shines, it comes out of the box with pretty much everything you need.

go test is a great, albeit basic, framework for testing. No need to become a domain expert in JUnit, or choose between nose and unittest. For convenience, there are testing assertion libraries like testify that you can pull in, but “you ain’t gonna need it”.

Similarly, Go’s sync package covers most synchronization primitives that you could reasonably need, http provides a production-ready HTTP server and client (even supporting HTTP2), and there’s enough goodies in the encoding package to shuffle data around in json, xml, csv, and a number of other common formats.

Formatting, Formatting, Formatting

Finally, I’d be remiss if I didn’t mention gofmt. I’m a bit of a nitpicker when it comes to formatting. I don’t particularly care which rules are applied, but I care a lot about consistency. Go’s internal formatting tool, gofmt, is adopted widely by the community and provides just enough consistency that your Go code “looks” idiomatic without feeling like you’re being suffocated by a linter.

Personally, I’ve found that reading open source Go code is a lot easier due to gofmt. Each project still has its own way of doing things, but every project follows the same formatting conventions. Aesthetically, this makes me happy.

So, I like Go. There was also a time that I was super enthralled by the Zen of Python and would turn my nose at other languages. I’m looking forward to a strong continued future of Go, but I could definitely see myself jumping on the Rust bandwagon once that language settles a bit, because I do love me some functional-ish programming.

For now, I’m a Gopher.