Part 1 demonstrated a workflow for Go and React development using Docker and Makefiles. This post covers transitioning to Go.
We focus on:
While deploying apps to the cloud I realized quite quickly that many of the tools I used were written in Go. Tools including Drone, Portainer, Docker, Kubernetes, Terraform and Traefik. I began to wonder: What was I missing? What was so special about the language?
Ken Thompson, Rob Pike and Robert Griesemer, are the original creators of the language. All have amazing credentials. They are the same people behind Unix, UTF-8, the B programming language (predecessor to the C programming language) and the Hotspot JVM. Checkout out creating the Go programming language on Go Time.
Concurrency is incredibly difficult to get right even when there is language support. Go has concurrency primitives called goroutines and channels designed for web scale. Go is a modern programming language that makes it easy to utilize all the cores on a multi-core machine.
Go has fast compile times because of dependency analysis. In Go unused dependencies are compile errors not warnings. This guarantees that no extra code will be compiled during builds, minimizing compilation time. Circular dependencies are also prohibited in the language, keeping the dependency tree lean and clean.
The standard built-in library has over 100 packages and each package is well documented.
Your code and its dependencies are compiled into a single static binary. The static binary can be reduced in size by specifying the target operating system and adding additional flags to remove debugging information, for example:
❯ go build ./cmd/api ❯ ls -l api -rwxr-xr-x 1 ivorscott staff 13035460 Jun 7 23:36 api ❯ GOOS=linux go build -ldflags="-s -w" ./cmd/api ❯ ls -l api -rwxr-xr-x 1 ivorscott staff 9613312 Jun 7 23:36 api
In combination with Docker you can achieve super light weight images, making Go applications great for container deployments.
Go tooling provides many commands for many development concerns. To name a few, there’s
go test for testing,
go mod for module support and
go tool pprof for code profiling.
Even Ryan Dahl, the creator of Node and Deno, abandoned Node for Go. Deno was originally written in Go before being replaced with Rust but the Go UX remained. For example
deno fmt comes from
go fmt which is a configure-less code formatter.
Go comes with type safety by default. It’s a statically typed language with an unconventional type system and that’s a good thing.
Migrating from Node to Go is not without its challenges. I am still making the transition myself. Beyond language syntax and mechanics, I faced 3 challenges building production ready services.
In Go, you need develop your own set of rules about where packages belong and stick to them. You need to remember that your application won’t compile if you have circular dependencies. In other words, two packages can’t cross import one another. This keeps dependency chains clear as well as your mental model of the codebase. It also forces you to consider where initialization occurs in your app. Folders also have a special purpose in Go. Folders are modules, components, or features with distinct APIs. Not a means to organize code arbitrarily. Folders represent unique and purposeful packages containing exported and unexported identifiers.
Rolling your own solutions with standard Go libraries takes time but how it works is transparent because you built it. Using a framework takes arguably less time and comes with a rich feature set, but it’s not always clear what happens under the hood. With a full-featured library you might only use a fraction of the available features.
An ORM like GORM can get you to your destination faster but it comes with a cost. GORM can make more queries than otherwise needed under the hood. Going ORM-less already sets you up for taking full advantage of the power of Postgres with raw SQL queries. Plus, ORMs can’t possibly cover every use case and you may end up locked in and without some functionality in complex scenarios. Futhermore, ORMs must be learned. You can’t carry that knowledge to other projects if you’re not using the same tool. SQL knowledge on the other hand is always relevant between relational databases. SQL is transparent. With raw SQL you never hide the cost of your queries.
Every new go developer should read Effective Go on the official Go web page. It summarizes idiomatic Go development, in other words, the Go way to do things. Also checkout Todd McLeod’s Udemy course, Learn How To Code: Google’s Go Programming Language, a comprehensive guide to the language fundamentals. For a more project based book tutorial try Let’s Go by Alex Edwards. Another book worth mentioning is The Go Programming Language by Alan A. A. Donovan and Brian W. Kernighan, it’s a classic Go book. I personally enjoyed its thorough explanation of the language and have a copy on my desk.
Even with these sources, I was still eager to find a production ready API service example. Not a web app and nothing basic. I wanted a complete guide I could tinker with. Ardan Labs scratched my itch. Thankfully, I heard about them last year and attended a workshop in Berlin. It placed me on the right track. I highly recommend their workshops and courses.
The next post in this series illustrates an updated Docker workflow for a production ready Go service.