Introduction
Part 1 demonstrated a workflow for Go and React development using Docker and Makefiles. This post covers transitioning to Go.
We focus on:
Why Go?
1. Many Tools Are Written In Go
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?
2. Renowned Creators
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.
3. Simple Concurrency Model
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.
4. Fast Compilation
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.
5. Proper Built-in Tools
The standard built-in library has over 100 packages and each package is well documented.
6. Light-weight images
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.
7. Tooling and UX
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.
8. Type Is Life
Go comes with type safety by default. It’s a statically typed language with an unconventional type system and that’s a good thing.
Challenges
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.
1. Rethinking The Way An App Is Structured
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.
2. Choosing Between Built-in Libraries Or 3rd-party Packages
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.
3. Choosing Between An ORM Or Using Raw SQL
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.
Training
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.
First day of training has been intense! #golang #ultimategotraining pic.twitter.com/wdEQ5MrxK0
— ivorscott (@ivorsco77) October 10, 2019
The next post in this series illustrates an updated Docker workflow for a production ready Go service.