I’ve spent the last 4 months programming mostly in Go. In the past, I’ve programmed mostly in Python (and a bit of C#, C++ & JavaScript), so getting started with Go wasn’t all too difficult coming from Python. I found that Go had several quality-of-life improvements that have led to it being my favourite programming language now.

What is Go?

If you’ve never heard of Go, no worries. Here’s a quick recap 👇🏽

Go is a programming language created by 3 engineers at Google. These engineers wanted to overcome the complexity of C++ with a programming language that was easy to use/write while having fast compile times. In November 2019, the Go programming language was released.

The Go documentation describes Go as:

“a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.” Even a large Go program will compile in a matter of seconds. Plus, Go avoids much of the overhead of C-style include files and libraries.*

What is Go used for?

Go is a general purpose language. It can be used for many things; cloud computing, web development, dev ops, etc. Go is now used across several companies and industries today.

My workplace Monzo uses Go to build a scaleable backend consisting of thousands of microservices and I use a tool called Hugo, a static site generator written in Go, to build my websites.

So why has Go become so popular? What makes it better than other programming languages?

Let’s go over what Go has to offer and what advantages I noticed myself 👇🏽

Benefits of using Go

Go has many advantages over other programming languages but these were the top 5 reasons it stood out to me over other programming languages:

  1. Simple syntax

    Go’s syntax shares some traits with both Python and Ruby - it’s clean and simple to understand. Coming from a background where I’ve programmed primarily in Python, I found picking up Go to be simpler than I thought it would be. Part of this is due to Go’s strict formatting and styling. With Python, it’s quite common to see differences in how other developers approach a problem but with Go, since the language is a bit more barebones compared to other languages, I found that code written by other developers was a lot easier to read and understand.

  2. Performance

    Go can compile code extremely quickly compared to other high-level programming languages. There are several reasons why Go can do this. One of those is due to how Go handles imports. Go using ‘static linking’ to bundle a program’s dependencies into the compiled executable. High-level languages like C++ use ‘dynamic linking’ which links dependencies at runtime instead and can lead to longer compile times. Go programs are also faster to run because Go programs are compiled directly into machine code in advance while languages that require an interpreter to read source code and translate it into machine code on the fly. There can also be some additional overhead if the source code needs to be translated to bytecode first before it is translated by the interpreter. In addition to this, Go is a statically typed language. Statically typed languages generally have better performance than dynamically typed languages because variables and expression types of variables are determined and checked when the program is compiled to enhance memory safety and improve error detection to overall result in more reliable builds. This is something that dynamically typed languages like Python lack, where types are assigned at runtime instead, which could introduce errors later on, something that I’ve run into in the past when writing web scrapping scripts in Python.

  3. Concurrency

    One of Go’s most praised features is its ease of being able to implement concurrency (a way of doing multiple things at the same time) into programs. For example, here’s what a simple Go program that uses concurrency looks like (example from Go by Example):

    package main
    
    import (
     "fmt"
     "time"
    )
    
    func f(from string) {
     for i := 0; i < 3; i++ {
         fmt.Println(from, ":", i)
     }
    }
    
    func main() {
    
     f("direct")
    
     go f("goroutine")
    
     go func(msg string) {
         fmt.Println(msg)
     }("going")
    
     time.Sleep(time.Second)
     fmt.Println("done")
    }
    

    For comparison, here’s what the same code would look like in Python:

    import threading
    import time
    
    def f(from_str):
     for i in range(3):
         print(f"{from_str}: {i}")
    
    def main():
     f("direct")
    
     thread = threading.Thread(target=f, args=("thread",))
    
     lambda_thread = threading.Thread(target=lambda msg: print(msg), args=("going",))
    
     thread.start()
     lambda_thread.start()
    
     thread.join()
     lambda_thread.join()
    
     print("done")
    
    if __name__ == "__main__":
     main()
    

    From the two examples above, the Go version is a lot cleaner and easier to read compared to the Python version. Go supports concurrency through the use of goroutines and channels. Goroutines are functions that run independently from the function that invoked it. Channels are pipelines that are used for sending and receiving data from one goroutine to another. Overall, concurrency is Go is effective and simple to implement.

  4. Garbage collection

    Go has built-in garbage collection, unlike C and C++. A quick 101 on garbage collection - When working in low-level languages like C, developers are in charge of allocating memory for programs to store data. If a developer forgets to unallocate this memory when the program is done using it, it can lead to bugs within the program (memory leaks, early program termination, etc). Garbage collection removes the need for manually assigning memory to programs, allowing programmers to focus on their program’s logic. Think of this scenario - You have your kids leave their toys in the living room. Without garbage collection, you would need to put away all the toys after the kids have gone to bed to free up the space in the living room. With garbage collection, the toys would auto-magically be put away without you having to lift a finger (something I imagine all parents would want). Garbage collection is a nice quality of life improvement found in most high-level programming languages that I personally appreciate after my own experience of dealing with memory leaks in C++ when programming video games in the past.

  5. Backwards compatibility

    Go was built with backwards compatibility being a first-class feature from the very beginning. This means that programs written in older versions of Go wouldn’t have to be re-written when new API changes are introduced in newer versions of Go. I’ve heard of at least one instance where developers at a particular company built their entire stack on Python 2.x only to then plan a large migration to Python 3.x later on. If this stack was written it Go instead, there’s a high chance that this type of large migration might not have been needed. I’ve not had to deal with this type of situation in my career yet. While I have dabbled in some scripts written in Python 2.x, I was already writing most of my code in Python 3.x and avoided the need for rewriting code. While Go does handle backwards compatibility gracefully in most instances, that isn’t to say that there have not been any complications that have come with ensuring backwards compatibility. If you want to learn more about how Go prioritises backwards compatibility, I would recommend this talk by Russ Cox (one of the co-creators of the Go programming language), which goes over how new versions of Go and build without breaking old programs written in Go.

How Monzo uses Go

The main reason I’m learning Go at the moment is because it’s the main programming language used at Monzo by backend engineers. The entire backend is composed of thousands of little Go programs that are connected together (often referred to as a microservice infrastructure).

Go was chosen for creating Monzo’s backend for its speed. You can build and deploy mircoservices in a language like Python, but in banking where things need to be done quickly, Go ended up being the better choice.

Monzo’s codebase is stored in a single mono-repo (a single code repository), split into directories for each microservice. These individual microservices are owned by smaller teams who manage and maintain these services. This setup allows teams to build and ship changes quickly in small batches without having to redeploy the entire code base for each small change and affecting each other’s work. Instead, teams focus on updating (usually) one microservice at a time to minimise the possibility of issues.

Due to this infrastructure design choice, Monzo ships on average, over 1,000 code changes to production a week! Something that was unheard of in the banking industry just a decade ago.

Getting started with Go

There’s an endless list of resources for getting started in about any programming language and it’s easy to get overwhelmed.

My suggestion - Start with one resource and stick to that one until you’ve finished it. Once you’ve completed that, feel free to branch out into some other resources. Here’s what I have personally found really useful for getting up to speed with Go 👇🏽

  1. Learn Go with Tests
  2. Go by Example
  3. Gophercises
  4. The official Go website - Specifically A Tour of Go

Summary

While Go isn’t the perfect programming language, overall I think it’s a fantastic language that has several advantages compared to existing high-level programming languages. I’ll likely be spending a considerable amount of writing Go code going forward, and hopefully, I’ll be sharing some more Go posts as well ✌🏼