I learned to program in R, and for the first two years of my professional career worked exclusively in python. I maintained legacy java code occasionally, but didn’t (and don’t) have any desire to write new systems in java. I find the language ridiculously clunky and unintuitive. The ecosystem is madness; poor documentation pervades. So much code has been written in java that new developers are expected to automatically adopt frameworks like Spring and SpringBoot. I’m sure there are some programmers that enjoy writing Java code, but I am not one of them. I’d rather do requirements gathering than try to open up IntelliJ and invalidate every Maven cache on my system and then restart 4 times just to get imports to resolve. These are, of course, personal problems. There are many developers who write better code that I could ever imagine, and they do it in Java. I don’t understand these people. Perhaps they actually reason about the world as though everything were an object with methods to call. Or maybe they enjoy looking at 50 line stack traces, and get excited when they see that ... indicating another 50 lines were truncated. Well written code should be elegantly beautiful. That’s something Java will never be. And to those who say that Java 1.8+ fixes these issues, I say, “no”. No matter how much it’s redesigned, its past will forever harbor the unslayable dragons of poor design.

Learning Go was an entirely different experience. Documentation is centralized with https://golang.org/pkg/ and https://pkg.go.dev/; and it’s designed to force you to write simple, clear code. Unlike java, which requires an extremely specific object oriented way of thinking about problems, I didn’t feel constrained by the language . With Go I could use a function, a method, a closure, etc. The language also includes the best set of tooling of any language I’ve worked in, the go tool. It trivializes the accessory elements of software development (running tests, handling dependencies, building, etc), so that you can focus on programming, not on configuring Maven. Best of all, Go programs compile into native machine code, so there’s no need to deal with a VM, like the JVM, which is usually fine until it isn’t, at which point you need to decide whether to tune it to get your program to run correctly or whether you should just blow your brains out.

I recently read Mat Ryer’s Go Programming Blueprints, 2nd Edition in order to familiarize myself with elements of the language that I don’t encounter on a day-to-day basis. The book is well written with chapters broken into consumable projects. This leaves the reader with a sense of accomplishment at the end of each chapter. Most chapters have a web server component, after all, this is what Go was built for. Ryer covers a lot more than just web servers, with sections devoted to cmd line utils, Google App Engine, and deploying apps using Docker.

If there’s one thing that I took away from the book, it’s this: Go is the language to use if you’re writing a web server. This is why:

package main

import "net/http"

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/hello/", func(rw http.ResponseWriter, r *http.Request) {
		rw.Write([]byte("Hello World"))
	})

	http.ListenAndServe(":8080", mux)
}

Saving that program in a directory named api, running go build, and then ./api is all you need to do to spin up a webserver at http://localhost:8080. For someone that comes from the data engineering side of the industry (read: doesn’t build web servers for a living), that’s ridiculously simple, and it’s all written in the standard lib. No framework needed. There are actually websites that exist for generating Java code which will do the same thing1. With autocomplete and autoimport enabled in my editor, this code took me maybe 45 seconds to write, and it’s a single, 12 line, 225 character, file. No config or project level files needed.

One of the things that’s readily apparent from this code is how readable Go code is (at least the std lib). Say you wanted to implement another route. You could check the documentation, or you could just read the code and see that we should add a new handler (with signature: func(rw http.ResponseWriter, r *http.Request)) to the multiplexer and register it at the route.

That readability hints at the thought that’s gone into building this language. It truly is a language built for making software engineering simpler, and that makes it a joy to work in. Ryer’s book highlights the important components of the language, especially for building web servers, and teaches the correct (i.e. simplest and cleanest) ways of leveraging those components.