Over the past couple months, I have spent a notable amount of time using Go to build APIs and CLI tools. Over this time, there are several features of Go that increasingly stood out to me as wonderful and made me truly enjoy writing in the language. I’ve heard many people share their opinion on the ups and downs of Go, so I wanted to make this post to share my opinion.
1. Wonderful Package Manager #
I’m convinced the package manager is the best thing in the world. The key thing that sets Go’s package manager above other managers like npm, PyPI or maven is that there is no need to publish packages to an official index, since packages can be downloaded from a git repository. For instance, if I wanted to download Gin, a popular web framework, I would only need to do two steps:
- Add the import statement
import ("github.com/gin-gonic/gin")
- Run the command
go get github.com/gin-gonic/gin
And with just this, I will be able to use the package. This makes it far easier to publish your own packages, as there is no need to upload them to an official package index. Additionally, your package will be automatically documented from internal comments on pkg.go.dev
, making it both easy to document your own packages and have a central repository of documentation for other packages. This encourages engineers to create and and publish packages, enhancing Go’s ecosystem.
2. Forced error handling #
If you have ever read or wrote a Go program, you are probably very familiar with the line if err != nil {}
. In Go, functions that may cause errors typically return a value of type error
, which is made possible by the fact that functions in Go can return several values of different types. This encourages the developer to check for errors after every dangerous function call and handle them accordingly. Though this is definitely boilerplate, it’s definitely worth it.
Being able to return errors as variables allows for an elegant system where errors that occur at the top of the call stack cascade down to functions at the bottom, where they can be handled within the correct context. For example, consider a function called getDataFromDB()
that fetches some data some of the time. If this function causes an error while retrieving data for an API request, we would want to react by sending a response like 500 Internal Server Error
. However if this function caused an error in a unit test, we would want to fail the test. While this is possible in other languages, Go’s idiomatic way is among the simplest and easiest to manage and maintain.
3. Go makes it easy to build insightful logging systems #
Another consequence of Go’s error handling philosopy is that you naturally have excellent places to write logs. This also work’s in conjunction with Go’s amazing log
package, which allows high customisable and insightful logging systems to be set up in only a few lines of code. I benefitted from this a lot while designing an OAuth2 system for my todo API Taskmaster. I was running into an issue where the API would deny authorisation requests when they should have worked. Upon checking the API’s log file, I could see that the API reported an error that there was no token attached at all! Here was the exact line:
[GEN] 07:10:28 auth.go:59: Error validating token: token had length 0
This allowed me to pinpoint the exact place in the code that was causing the authorisation to fail, as well as allowing me to deduce (after some more troubleshooting) that my CORS middleware was blocking the authorisation header. Overall, error handling and logging work very well hand-in-hand and save a ton of time when debugging applications.
4. Wonderful testing API #
This one is pretty straight forward. Go has a very good, intuitive testing API that can be extended with user-made packages like testify
to create an easy, straight-forward way to write unit tests for code, encouraging developers to adopt proper tests.
5. Blazingly Fast Compiler #
This is one of Go’s most well-known advantages over other languages. Due to efficient dependency analysis, Go’s compiler is lightning fast. This makes it easy to rapidly prototype and iterate changes in programs. User-made tools such as the static site generator Hugo make use of this, enhancing developer experience significantly. Go’s optimised compiler is also one reason that Go was so successful in the language that powers Docker and Kubernetes.
6. Syntax #
Overall, this might be the biggest contributor to my liking of Go. The syntax feels modern and efficient being fairly concise with minimal boilerplate compared to languages like Java or C++. Solving problems idiomatically feels very fun in Go which development feel productive. Though this may be a bit controversial, I believe the opinionated formatting is also a good feature. It eases some of the burden of writing readable code while also making it easier to naturally write code that is easily readable by others, as well as making it easier to adapt to open-source codebases and other existing projects
7. Native concurrency #
Go’s concurrency model isn’t something I have much experience with (yet), though I can already see the benefits. During both of my API projects so far, I have not had to worry about async processes like fetching data from a database due to Go handling it internally. This makes writing solutions far easier from the developer side, as well as removing unnecessary complexity. Furthermore, I find Go’s channel system the easiest to understand out of the languages I have used so far.
So hopefully I have explained well the reasons that I like Go. I believe it to be an elegant, powerful, versatile language that is fit for many purposes and has many features that make it truly unique.