tracks / python-to-go
Python
Go

A guided path for experienced Python developers learning Go.

Move into a simpler, explicit compiled language.

This track is organized around mental translation, not beginner programming. Each lesson explains what Python instinct you are bringing with you, what Go usually replaces it with, and how to practice that shift immediately.

6 lessons~2.5 hrscomparison-firststatus: stableupdated Apr 2026

before you start

  • $You already write real Python code. This path assumes professional instincts, not beginner-level programming lessons.
  • $Expect comparison-first modules: each stop starts from familiar Python habits, then shows where Go agrees or pushes back.
  • $Plan to practice the target ecosystem directly — syntax, testing, and design choices should feel native by the end, not translated.

by the end you can

  • Read and write idiomatic Go without translating every line back through Python.
  • Spot which Python instincts transfer cleanly and which ones need a different Go mental model.
  • Use the six modules as a working checklist when you build your first real Go tool, service, or feature.

syllabus

6 modules, one capstone.

6 lessons · ~2.5 hrs
module 01
1 lesson · ~25 min

Mindset shift

Move from Python's dynamic, runtime-first habits to Go's compiled and explicit model.

module 02
1 lesson · ~25 min

Syntax and data

Translate functions, control flow, slices, and maps into Go's more explicit syntax.

module 03
1 lesson · ~25 min

Structs and methods

See how Go replaces many common Python class patterns with simpler building blocks.

module 04
1 lesson · ~25 min

Error handling

Replace exception-driven flow with explicit error values and predictable control paths.

module 05
1 lesson · ~25 min

Concurrency basics

Build intuition for goroutines and channels from a Python async and threading background.

module 06
1 lesson · ~25 min

Testing in Go

Translate pytest instincts into Go's standard testing package, table-driven tests, and subtests.

track reference

Keep the shared translation aids in one place.

These are track-wide lookup tables and task patterns, not lesson-specific reading. Use them when you need a quick reset on the recurring source-to-target language translations.

Data type mappings

Recheck the building blocks when a translation starts to wobble.

Python

string

Go

string

Common operations

  • Format values with f-strings in Python and fmt-based formatting in Go.
  • Uppercase or lowercase text with direct string helpers in both languages.
  • Treat strings as immutable values in both languages instead of editing them in place.

Python

list

Go

slice

Common operations

  • Append values with list.append(...) in Python and append(...) in Go.
  • Use comprehensions in Python but explicit loops plus append in Go when transforming collections.
  • Remember slices carry length and capacity semantics instead of behaving exactly like Python lists.

Python

dict

Go

map or struct

Common operations

  • Use dicts in Python for flexible keyed data and maps in Go for dynamic keyed data.
  • Prefer small structs in Go when the shape is known ahead of time.
  • Check for missing keys explicitly in both languages rather than assuming presence.

Python

None / missing value

Go

zero value, nil, or (value, err)

Common operations

  • Use None in Python for ordinary absence and explicit error values in Go for failure.
  • Check nil or zero values deliberately in Go rather than expecting exception-driven control flow.
  • Keep absence and failure distinct so the caller can read the contract clearly.

Comparative cheat sheet

Keep the most common tasks visible while you practice.

TaskPythonGo
Define a functiondef greet(name: str) -> str: return f"hello {name}"func greet(name string) string { return "hello " + name }
Format a stringmessage = f"user={user_id}"message := fmt.Sprintf("user=%d", userID)
Add to a collectionitems.append(value)items = append(items, value)
Loop over a collectionfor user in users: print(user["email"])for _, user := range users { fmt.Println(user.Email) }
Handle missing or failing datatry: user = load_user(user_id) except UserError as error: logger.error(error)user, err := loadUser(userID) if err != nil { log.Println(err) return err }
Test a functiondef test_slugify(): assert slugify("Hello") == "hello"func TestSlugify(t *testing.T) { if slugify("Hello") != "hello" { t.Fatal("unexpected value") } }

capstone

Ship a small Go project that proves the mental model stuck.

Build one focused artifact in Go using the same comparison-first habits from the track: start from a familiar Python shape, then deliberately redesign it the way Go expects.

  • Translate a familiar Python data flow into idiomatic Go structure instead of preserving the old shape by force.
  • Apply the early modules to data modeling, control flow, and API boundaries before you add tooling, polish, or deployment concerns.
  • Use the later modules to verify, test, and package the result the way Go developers expect to see it shipped.

ready?

Start with module 01 — Mindset shift.

Begin