My First Impressions of Go
Intro
I recently had to learn Go as a part of work. I’m typically more interested in functional programming languages, so I wasn’t particularly excited. Some features and idioms pleasantly surprised me, while others leave me a bit disappointed with the language.
Stepping into Go, I knew that it was something that people loved to hate. People seemed to compare it to Rust in many contexts, even though they seem to compete in different spheres entirely. With this, I jumped into Go with the impression that it should compare to Java, since they both are garbage collected, compiled languages, with a C-like syntax, and a rich standard library targeting (in my understanding) backend applications. This is also the general usecase in which I’d be working with it, so that’s where I can compare it.
I’m not going to mention speed; While there are outliers and honest-to-goodness “better” runtimes in some languages than others, many languages that power web or backend applications simply don’t have speed as a problem, typically.
Some of the “good” or “bad” notes are going to be personal preference, and you very likely may have had a different or opposite experience with that specific aspect of Go.
The Good
Minimal/simple syntax
This is the first thing that most people would notice, I imagine. I think the general syntax is pretty nice, clean, and clear. It’s pretty easy to see a line, and get a general idea of whats going on. I think my least favorite thing here is instantiation vs. assignment, but that’s not a big deal. I found it really easy to write “pure” functions and they flowed pretty naturally.
The static typing seems to be agreeable. I haven’t yet hit a point where I felt like it was getting in my way. I find the type declarations themselves to be nice as well, almost as nice as F# typing.
Fast compilation times
Go compiles fast. This is great; all of the benefits with compile time checks, but you’re still able to have a live update with tilt that’s quick enough to just compile the code and copy it into the docker container, which was cool to see. I think I might prefer hot-reloading built into the language, but at the end of the day the compilation is simply fast enough for it to not be a big deal
Low resource utilization
Memory usage is always a pain point for people with Java. And I don’t really blame them. I typically try not to be so concerned with high resource usage, but it’s borderline unacceptable at times that a runtime routinely uses as much memory as the JVM does, while idle. Seeing Go stay less than 100MB constantly for a simple web app was a breath of fresh air.
Low memory usage is also great for deployment. Assuming the application is going into Kubernetes, that means you can just have less physical resources per workload, reducing costs for the infrastructure. It’s the same deal with non-containerized workloads, if you’re into multi-tenant deployments.
The Bad
Tuple returns
I’m not a fan of the tuple returns everywhere. Constantly using the below pattern is just ugly and wastes vertical space on the screen.
foo, err := MyFunction
if err != nil {
// do something
}
I realize that there has to be some kind of error management and control, but I think that many other languages do this better. I think my favorite might be Elixir’s with
syntax, allowing you to define the “happy path” throughout the program and handle all the undesired returns in their own way at the bottom of the statement.
Only for
loops
Since I lean towards the functional side, it should be no surprise that I prefer higher-order functions and recursion over looping in the first place. While Go does support both of these, a simpler for
loop is the more common/preferred idiom. With generic typing, higher-order functions might be more common place, but I’d prefer to stay with the more common idioms when I write code that isn’t for my eyes only.
Default tooling
As someone who’s been a Linux user since high school, I’m not a fan of the go
command line breaking the --help
paradigm (so, go test --help
for example) and instead using go help test
. Elixir’s mix
CLI is also guilty of this. I’m sure they have their reasons, but I’m not a fan of Go breaking shell’s homogeneity with the --help
idiom.
Go is the only recent example of a language that I’ve learned that doesn’t include a REPL. I find it a bit tedious for me to have to open up the Go Playground just to test a function and check how it’s inputs and outputs look.
Overly-minimalist philosophy
Go sits on top of a throne of “Less is More”. Minimalism in a language is a very valuable trait, and while I think Go excels at minimalism, I also believe that it’s too much.
I think Lisp/Scheme excel at thoughtful minimalism; their macros (probably also dynamic typing) put all the power into the programmers hands, allowing them to add any syntax they want to achieve their goal or complete their project. And even though code is being written by these macros, the language is still almost entirely homoiconic.
With Go, I think many battle-tested patterns from other languages are thrown away in order to keep the mundane and inoffensive.
Conclude
Even with my complaints, I think Go is a good language. Working in a team that doesn’t share my appreciation for “funcy” languages, Go is pretty likely to be one of the first choices. It’s simple enough to get a basic grip on the syntax in an afternoon, but powerful enough to run the leading workload scheduling software.
A lot of the quirks and features of Go don’t exactly jive with me, but that doesn’t mean that they ruin the language or that it’s unusable. Solving problems at work doesn’t have to be super exciting, it just has to get the job done. I think that’s how I feel about Go.