I now consider every line of Go code I have ever written as deprecated code. In this blog post I am going to explain why I quit writing Go and what will be missing. First, I want to start with the things I really liked about Go.
Pros of Go
Message Passing Natively Supported
Go’s capabilities of utilizing leightweight threads and communicating between them using message passing is what really got me into learning the language in the first place. It seemed like a simple, C-derived alternative to Erlang. Although that comparison might overestimate Go’s power, Go was good enough for implementing the backend server for my company’s remote maintenance tool. This tool acts as a middleman between a browser on the one end and one of our company’s boards on the other end, as seen in the image below.
The implementation turned out to be a little more complicated than I initially expected, but after having completed the project, Go got me hooked.
Truly Fat Binaries
When first deploying my Go application I was surprised how easy it was. Simply upload the binary to the server or include it in the container image, add the configuration as well as static assets and you are done. Go’s fat binaries bundle the runtime environment, the libraries as well as your application making them a joy to deploy. There is no more need for complex runtime management and so on.
Of course, you need to create a run user, add a firewall exception, ensure a database backup, etc., but this are one-time jobs and are not related to programming.
Supports BSDs and Windows too
At my company we run both Linux and Windows servers. Basically, the Go compiler works the same way accross all operating systems. A simple
go install <applicationName> is all you need to execute on your compile system, be it Windows or Linux. As mentioned previously, the fat binaries ease the shipping process a lot.
Cons of Go
I’ll start with the argument that destroyed a lot of my joy of working with Go.
Strict Enforcement of the Google Code Guidelines
The Go compiler is repressively enforcing a mix of Google’s C++ and Python style guidelines. For example placing an opening curly bracket on a new line causes the compiler to emit a syntax error. The same goes for string concetanations that start on a new line. With updates of Visual Studio Code Go plugin automatic refactoring was turned on by default and the linter displayed so many warnings due to my code style, that I turned it off.
According to my opinion, a programming language should let a developer use his or her style. A heavy influence on the programmer’s style is okay, but a fanactic, religious-like enforcement is inacceptable. My style is the result of best practices from my teachers as well as experiences made in my career and I don’t want to completely change it, just because Google thinks so.
Broken Package Management
Go’s included “package manager” works implicitely by resolving the dependencies directly from your source code and pulling the required code from your local file system or from git repositories using HTTP. This is really nice in theory, but has the devestating disadvantage that there is no way to determine the version of a package. Go solves this issue by simply pulling the master branch. If breaking changes are introduced in the master branch, your application won’t compile or work on a fresh install any more.
A possible mitigation strategy is shown by the Kubernetes project whose creatores store their application’s dependencies in their repository. However, this is an anti pattern of using a VCS.
Despite being highly controversial, I prefer to model similar behaviour of objects by using class hierachies. Go willingly chose to not support this behaviour causing me to write a lot of duplicated code. For example, my SAP analysis web interface uses three different product group structures, as the required amount of details varies between user stories. Go forces me to maintain the same code in three different places.
Interfaces help to solve the problem with methods of objects, but not with data members. Additionally, I don’t like the implicity of interface implementations, as most editors are not capable of providing a helpful suggestion and you have to do a lot of manual documentation lookups.
Dealing with collections feels really cumbersome compared to Ruby or Python, despite of being inspired by the latter one and Go’s claim to provide a dynamic language-like feeling. This issue as well as others could be solved by introducing generics. However, this is refused by Go’s creators and its community by using very weird arguments (see the last paragraph of the second bullet point).
Feature-Lacking HTTP Library
For creating simple echo services, Go’s built-in HTTP library is fine. However, for adding REST-style parameters, cookies or sessions additional plug-ins like the Gorilla Toolkit are required. In the documentation there is no mentioning of those missing things, you expect from a modern HTTP library.
Additionally, setting the supported TLS ciphers is quite cumbersome.
A further issue is the bad error handling. For example, the default Dropwizard HTTP client has GZIP compression turned on by default. However, Go’s HTTP library did not recognize the GZIP headers and tried to read the compressed payload as plain data. This was the start of an hour of troubleshooting.
All in all, I like Go and its minimalistic take on programming. However, Go turned out to be the wrong tool for me. Nevertheless, I am going to continue to use tools made with Go, like Hugo (this article was rendered with it), Docker and Kubernetes.