Eight visual lessons that take you from print("hello")
to writing real Go services. Built for Python developers - lots of side-by-side code, diagrams for every tricky concept, and a 5-year-old explanation when you need one.
You already write Python. So why learn Go at all? Three reasons that matter in practice:
| If you care about | Python gives you | Go gives you |
|---|---|---|
| Speed | Interpreter overhead. Fast to write, slow to run. | Compiled native binary. Predictable performance. |
| Concurrency | The GIL serializes threads. asyncio is powerful but viral and awkward. | Goroutines are dirt cheap. One go f() spawns concurrent work. Channels for safe communication. |
| Deployment | "Works on my machine" with the right venv, Python version, and 47 wheels. | One static binary. Copy it to a server and run. |
| Types | Optional type hints, checked by an external tool (mypy) you might forget to run. | Types are mandatory and checked by the compiler. |
| Ecosystem fit | Data science, ML, scripting, web (Django/Flask). | Cloud-native: Kubernetes, Docker, Prometheus, Terraform, etcd are all in Go. |
Eight bite-sized lessons. Each builds on the previous, but each also stands alone if you need a refresher later.
Your first Go program. How package main works. go run vs go build. Why Go compiles instead of interprets.
Static typing without the pain. var, const, the := shorthand. Zero values (the death of None).
Go has only one loop keyword: for. Plus if with init, smart switch, and defer (a superpower).
Multiple return values (no more tuples!), named returns, variadic args, closures. Functions are first-class.
5Arrays vs slices vs maps. How slices secretly share memory (the famous gotcha). Iterating with range.
Go's "classes that aren't classes." Methods. Pointer vs value receivers. Embedding instead of inheritance.
7No try/except. Errors are values you return and check. panic/recover for the rare emergency.
go.mod, imports, why Capitalized names are public, and how to use the standard library.
Each lesson has the same shape so you always know what to expect:
| Section | What it gives you |
|---|---|
| The 5-year-old version | A plain-English analogy in an orange box. Click to expand; skip if you don't need it. |
| Python you know ↔ Go | Side-by-side code. Blue = Python, teal = Go. Read both. Spot the differences. |
| What's different and why | Short prose explaining the mental shift. This is where the real learning happens. |
| Diagram | Visual model for concepts text struggles with (slice internals, defer stacks, etc). |
| Real example | Something from a real codebase: an HTTP handler, a CLI flag, a k8s reconciler snippet. |
| Gotchas | Mistakes Python devs make in Go. Red boxes. Read them twice. |
| Glossary | Terms used in the lesson, anchored to the page. |
You can do everything in this course on go.dev/play without installing anything. To write real programs, install Go locally:
# install
brew install go
# verify
go version
# go version go1.23 darwin/arm64
# Linux: download from go.dev/dl, extract to /usr/local
tar -C /usr/local -xzf go1.23.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
# Windows: download the MSI from go.dev/dl, double-click
Editor: VS Code + the official Go extension. Autocomplete, jump-to-definition, format-on-save, inline errors. Or use whatever you already use - gopls (the Go language server) plugs into most editors.
Save this. If you're confused mid-lesson, come back here.
| In Python you say… | In Go you say… | Notes |
|---|---|---|
x = 5 | x := 5 or var x int = 5 | Go infers the type. := declares + assigns. |
def add(a, b): return a + b | func add(a, b int) int { return a + b } | Types after the name. Return type after the signature. |
my_list = [1, 2, 3] | nums := []int{1, 2, 3} | A slice. Looks like a list, behaves slightly differently (lesson 5). |
my_dict = {"a": 1} | m := map[string]int{"a": 1} | Maps in Go are typed: key type and value type. |
class Dog: ... | type Dog struct { ... } | No classes. Just structs + methods. Composition over inheritance. |
try: ... except: ... | x, err := f(); if err != nil { ... } | Errors are return values, not exceptions. Check every one. |
None | nil | Same idea. But primitives like int can't be nil; they default to 0. |
print("hi") | fmt.Println("hi") | Functions live in packages. fmt is the formatted-I/O package. |
import requests | import "net/http" | Import paths are strings. Standard library covers HTTP, JSON, crypto, etc. |
async def f(): ... | go f() | Run any function concurrently with go. (Concurrency is a later course.) |
| Term | Meaning |
|---|---|
go (the tool) | The Go command-line tool. Runs, builds, tests, formats, manages dependencies. One CLI for everything. |
gopls | The Go language server. Powers IDE features (autocomplete, jump-to-definition, refactor). |
go.mod | The "requirements.txt + setup.py" of Go. Declares module name and dependencies. |
| Module | A versioned bundle of related Go packages. Like a Python distribution package. |
| Package | A directory of .go files sharing a namespace. Like a Python module. |
| Goroutine | A lightweight concurrent function. Like a thread but ~2 KB to spawn. Managed by the Go runtime. |
| Channel | A typed pipe for safely passing values between goroutines. The Go answer to queue.Queue + locks. |
| Zero value | The default value of a freshly declared variable. 0 for numbers, "" for strings, nil for pointers/maps/slices. |
| Receiver | The "self" of a Go method, but spelled before the method name: func (d *Dog) Bark(). |
| Interface | A set of methods. Any type with those methods implicitly satisfies the interface (no implements keyword). |