A love letter to Go

The Go programming language (or golang) left beta twelve years ago, and version 1 was released which means I’ve been writing Go on and off for 12 years now.

Go came along at the perfect time. I was in the middle of evaluating languages for work as we were experiencing scaling issues with an existing codebase and needed a new approach. My simple shortlist of requirements was:

  • Performant
  • Statically-typed
  • Supports concurrency
  • Runs on Linux
  • Simple to learn, write and maintain
  • Preferably C-like syntax.

We considered many options, from C++ and Java to Scala and Haskell, but nothing fit the problems we were trying to solve as a business. At this point, languages like Rust and Typescript didn’t exist, so they weren’t evaluated.

Go ticked many of our boxes, and the initial prototyping went smoothly, so we decided to try it out.

We started replacing the most problematic parts of the codebase with Go services. Our data pipelines, previously part of the monolith, began to show cracks as the platform grew, so they were our first candidate.

The results were spectacular. The code outperformed our expectations regarding speed and resource consumption, and it was written by a team that had never used Go before and had become productive very quickly. At this point, I was sold on Go as a primary backend language.

Fast-forward 12 years, and I predominantly write in Typescript at work. I’m technology agnostic when picking who I work for. Give me an interesting problem over a specific language any day.

Getting into the guts of Typescript for the first time in my career has given me a new appreciation for Go that I may have previously taken for granted and a couple of insights into places it could improve. So, with the “absence makes the heart grow fonder” spiel out of the way, here’s what I love about Go.

Simplicity

The Go team kept simplicity at its heart when designing the language. Go has only 25 keywords, which can be held in your head without looking them up. It also has a toolchain built into the language for compiling, testing, and formatting. You can get a long way in Go with just the runtime and nothing else.

It’s so simple and compiles so quickly that it is now my go to scripting language for one-off jobs or small programs that live on my laptop, most of which aren’t even compiled and are executed using go run. This is unheard of for a statically typed, compiled language. Imagine doing that in C++ or Java.

The standard library

The Go standard library is a thing of beauty. It contains all the building blocks needed for modern software development: network protocols, file handling, databases, encoding formats, etc. It’s also wholly readable to end users. Some of the best examples of how to write Go and solve problems are in the library. The standard library is one of the reasons Go engineers are less likely to pull in dependencies than other languages.

Interfaces

The standard library contains many interfaces that control function access. These are small types with one or two function definitions. Once you grasp the power of Go’s small, duck-type interfaces, a whole world of simplicity opens up. Interfaces allow you to decouple code, swap out implementations, simplify testing and interact with the standard library in an idiomatic way.

Error handling

Error handling is one of the things that people don’t like about Go. It’s verbose, explicit and the majority of functions return an error along with the value, if they return a value at all. If err != nil is the most memed part of the language as it’s so ubiquitous in every codebase.

I love it. Combine it with newer language features such as the As / Is / Join functions and the structured logging package, and you have a powerful way of bubbling errors up through application code. Give me this over try / catch exceptions any day of the week.

I also tend to lean on TDD when writing certain types of code. An error return type lets you quickly work through edge cases with early exits and build robust functions.

Concurrency

The decision to add a powerful concurrency model into the language is one of the Go killer features. I also appreciate that the Go team baked this in as a first-class citizen in a very opinionated way. Unlike Rust, which leaves implementation up to the community, or languages that require you to manage multithreading, Go has a defined way of achieving concurrent execution using goroutines, channels, mutexes and waitgroups. It’s simple, powerful, and makes it easy to build highly performant systems.

Where could Go improve

I’m pretty happy with where Go is. My main problem with the language, namely package management, was solved with modules. I don’t write much library code, so I’ve rarely needed iterator functions or generics. However, I appreciate that generics were introduced after the core team listened to the community. I also don’t believe these features make the language more complex. They’re there if you need them; if not, you can ignore them.

One place I think the language could improve is working with data structures. The way other languages handle iteration with maps and filters is far superior to the slices and loops of Go. It’s one thing that stands out in Rust code that makes me slightly jealous. People have built abstractions in Go to do this on top of slices, but I’d love it to be part of the language.

Another thing that I’d love to see, which is not a language feature, is better adoption in data science and machine learning. Go makes perfect sense for the operational side of data - moving it from A to B, processing it, and storing it. But when it comes to analysing it, a wave of fear comes over me as I know I’ll have to spend the morning figuring out why my Python environment has fallen apart since the last time I used it.

Fin

So there you have it. A getting-shit-done, general-purpose language that may not have the speed of C, the memory safety of Rust, the flexibility of Typescript, the data ecosystem of Python or the widespread adoption of Java, but yet holds its own and continues to evolve.

Go. Not everyone’s cup of tea, but it’s very much mine.