During its decade-plus years in the wild, Google’s Go language, aka Golang — with version 1.18 out as of March 2022 — has evolved from being a curiosity for alpha geeks to being the battle-tested programming language behind some of the world’s most important cloud-centric projects.
Why was Go chosen by the developers of such projects as Docker and Kubernetes? What are Go’s defining characteristics, how does it differ from other programming languages, and what kinds of projects is it most suitable for building?
In this article, we’ll explore Go’s feature set, the optimal use cases, the language’s omissions and limitations, and where Go may be going from here.
Go language is small and simple
Go, or Golang as it is often called, was developed by Google employees — chiefly longtime Unix guru and Google distinguished engineer Rob Pike — but it’s not strictly speaking a “Google project.” Rather, Go is developed as a community-led open source project, spearheaded by leadership that has strong opinions about how Go should be used and the direction the language should take.
Go is meant to be simple to learn, straightforward to work with, and easy to read by other developers. Go does not have a large feature set, especially when compared to languages like C++.
Go is reminiscent of C in its syntax, making it relatively easy for longtime C developers to learn. That said, many features of Go, especially its concurrency and functional programming features, harken back to languages such as Erlang.
As a C-like language for building and maintaining cross-platform enterprise applications of all sorts, Go has much in common with Java. And as a means of enabling rapid development of code that might run anywhere, you could draw a parallel between Go and Python, though the differences are far greater than the similarities.
Go language has something for everyone
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.
Go makes the developer’s life easy in a number of ways.
Go is convenient
Go has been compared to scripting languages like Python in its ability to satisfy many common programming needs. Some of this functionality is built into the language itself, such as “goroutines” for concurrency and threadlike behavior, while additional capabilities are available in Go standard library packages, like Go’s http package. Like Python, Go provides automatic memory management capabilities including garbage collection.
Unlike scripting languages such as Python, Go code compiles to a fast-running native binary. And unlike C or C++, Go compiles extremely fast—fast enough to make working with Go feel more like working with a scripting language than a compiled language.
Further, the Go build system is less complex than those of other compiled languages. It takes few steps and little bookkeeping to build and run a Go project.
Go is fast
Go is portable
Executables created with the Go toolchain can stand alone, with no default external dependencies. The Go toolchain is available for a wide variety of operating systems and hardware platforms, and can be used to compile binaries across platforms.
Go is interoperable
Go delivers all of the above without sacrificing access to the underlying system. Go programs can talk to external C libraries or make native system calls. In Docker, for instance, Go interfaces with low-level Linux functions, cgroups, and namespaces, to work container magic.
Go is widely supported
The Go toolchain is freely available as a Linux, MacOS, or Windows binary or as a Docker container. Go is included by default in many popular Linux distributions, such as Red Hat Enterprise Linux and Fedora, making it somewhat easier to deploy Go source to those platforms.
Support for Go is also strong across many third-party development environments, from Microsoft Visual Studio Code to ActiveState’s Komodo IDE.
Where Go language works best
No language is suited to every job, but some languages are suited to more jobs than others. Go shines brightest for developing the following application types.
Go’s concurrency and networking features, and its high degree of portability, make it well-suited for building cloud-native apps. In fact, Go was used to build several cornerstones of cloud-native computing including Docker, Kubernetes, and Istio.
Distributed network services
Network applications live and die by concurrency, and Go’s native concurrency features — goroutines and channels, mainly — are well suited for such work. Consequently, many Go projects are for networking, distributed functions, and cloud services: APIs, web servers, minimal frameworks for web applications, and the like.
Utilities and stand-alone tools
Go programs compile to binaries with minimal external dependencies. That makes them ideally suited to creating utilities and other tooling, because they launch quickly and can be readily packaged up for redistribution. One example is an access server called Teleport (for SSH, among other things). Teleport can be deployed on servers quickly and easily by compiling it from source or downloading a prebuilt binary.
Go language limitations
Go’s opinionated set of features has drawn both praise and criticism. Go is designed to err on the side of being small and easy to understand, with certain features deliberately omitted. The result is that some features that are commonplace in other languages simply aren’t available in Go — on purpose.
One longstanding complaint was the lack of generic functions, which allow a function to accept many different types of variables. For many years, Go’s development team held out against adding generics to the language, on the grounds that they wanted a syntax and set of behaviours that complemented the rest of Go.
But as of Go 1.18, released in early 2022, the language now includes a syntax for generics. The lesson to be drawn is that Go adds major features rarely and only after much consideration, the better to preserve broad compatibility across versions.
Another potential downside to Go is the size of the generated binaries. Go binaries are statically compiled by default, meaning that everything needed at runtime is included in the binary image. This approach simplifies the build and deployment process, but at the cost of a simple “Hello, world!” weighing in at around 1.5MB on 64-bit Windows.
The Go team has been working to reduce the size of those binaries with each successive release. It is also possible to shrink Go binaries with compression or by removing Go’s debug information. This last option may work better for stand-alone distributed apps than for cloud or network services, where having debug information is useful if a service fails in place.
Yet another touted feature of Go, automatic memory management, can be seen as a drawback, as garbage collection requires a certain amount of processing overhead.
By design, Go doesn’t provide manual memory management, and garbage collection in Go has been criticised for not dealing well with the kinds of memory loads that appear in enterprise applications.
That said, each new version of Go seems to improve the memory management features. For example, Go 1.8 brought significantly shorter lag times for garbage collection. Go developers do have the ability to use manual memory allocation in a C extension, or by way of a third-party manual memory management library, but most Go developers prefer native solutions to those problems.
The culture of software around building rich GUIs for Go applications, such as those in desktop applications, is still scattered.
Most Go applications are command-line tools or network services. That said, various projects are working to bring rich GUIs for Go applications. There are bindings for the GTK and GTK3 frameworks. Another project is intended to provide platform-native UIs, although these rely on C bindings and are not written in pure Go. And Windows users can try out walk.
But no clear winner or safe long-term bet has emerged in this space, and some projects, such as a Google attempt to build a cross-platform GUI library, have gone by the wayside. Also, because Go is platform-independent by design, it’s unlikely any of these will become a part of the standard package set.
Although Go can talk to native system functions, it was not designed for creating low-level system components, such as kernels or device drivers, or embedded systems. After all, the Go runtime and the garbage collector for Go applications are dependent on the underlying OS. Developers interested in a cutting-edge language for that kind of work might look into theRust language.
Go language futures
Go’s future development is turning more towards the wants and needs of its developer base, with Go’s minders changing the language to better accommodate this audience, rather than leading by stubborn example. A case in point is generics, finally added to the language after much deliberation about the best way to do so.
The 2021 Go Developer Survey found Go users were on the whole happy with what the language offers, but also cited plenty of room for improvement. Top areas in which Go users wanted improvements were dependency management (a constant challenge in Go), diagnosing bugs, and reliability, with issues like memory, CPU usage, binary sizes, and build times ranking much lower.
Most languages gravitate to a core set of use cases. In the decade Go has been around, its niche has become network services, where it’s likely to continue expanding its hold. By and large, the main use case cited for the language was creating APIs or RPC services (49 per cent), followed by data processing (10 per cent), web services (10 per cent), and CLI applications (eight per cent).
Another sign of the Go language’s growing appeal is how many developers opt for it after evaluating it. 75 per cent of those polled who considered using Go for a project chose the language. Of those who didn’t choose Go, Rust (25 per cent), Python (17 per cent), and Java (12 per cent) were the top alternatives.
Each of those languages has found, or is finding, other niches: Rust for safe and fast systems programming; Python for prototyping, automation, and glue code; and Java for long-standing enterprise applications.
It remains to be seen how far Go’s speed and development simplicity will take it into other use cases, or how deeply Go will penetrate enterprise development.
But Go’s future as a major programming language is already assured — certainly in the cloud, where the speed and simplicity of Go ease the development of scalable infrastructure that can be maintained in the long run.