The Part Tutorials Get Wrong
After years of living in TypeScript and React, I decided it was time to learn Go properly.
The tutorials I found all had the same shape: three chapters on variables and loops, a section on if statements, and — if I was lucky — one paragraph about error handling buried at the end. So I could write a for loop in Go within ten minutes, but the first time I met cfg, err := loadConfig(), I had to stop and stare.
That's when it clicked: tutorials optimize for beginners, and I am not a beginner — I'm a beginner in this language, not in programming. What I actually needed was a map from the mental models I already have to the new syntax. Nobody sells that map.
So I built one — as a playbook for my AI agents.
The 4-Pillar Framework
cross-lang-analogy is part of my agent-playbooks repo (see the previous post). It activates whenever I ask about a new language, and it forces every single explanation through four pillars:
- Literal Syntax Reading. Before any terminology, the unfamiliar code gets translated into plain human speech, line by line.
:=becomes "declare this and infer the type" — not operator trivia to memorize. - Familiar-Language Equivalent. The exact same logic, side by side, in the language I already think in.
- Paradigm Shift & the "Why". The important one. Why doesn't this language do it the familiar way? What value is the design protecting?
- False Friends & Gotchas. Habits from my old stack that compile fine in the new one — and detonate at runtime.
What It Looks Like in Practice
Asking "how does error handling work in Go?" with this playbook active gets me something very different from a tutorial. First the literal reading: Go functions return two values, and the second one is an error — nil means success. Then the side-by-side:
// TypeScript: errors travel through exceptions
try {
const config = loadConfig();
} catch (err) {
// handle it — or don't, the compiler won't insist
}
// Go: errors are ordinary return values
config, err := loadConfig()
if err != nil {
return fmt.Errorf("loading config: %w", err)
}
Then the "why": in Go, errors are values, so they appear in the function signature. Failure is part of the type contract, not a hidden control-flow channel. That reframing alone did more for my Go than any syntax drill.
And finally the false friend — my favorite pillar. Coming from TypeScript, I can ignore err and just use config. It compiles. But config may be nil, and there is no catch coming to save me — just a runtime panic. Knowing that specific trap before writing Go is worth more than a hundred pages of "getting started".
One Anchor, Different Lessons
There's a small design detail in the playbook I'm fond of: Phase 1 is anchor discovery. It asks which language you know best before explaining anything — because the same Go concept needs a different bridge for a Python developer (where's my duck typing?) than for a Java developer (where's my class hierarchy?).
The anchor language isn't background knowledge — it's the coordinate system. Change the anchor, and every analogy changes with it.
Lessons Learned
- Learning a new language is mostly translation, not memorization — map the paradigms, don't grind the syntax.
- The "why" beats the "how". Paradigm shifts (errors as values, ownership, CSP concurrency) are where real understanding lives.
- Gotchas are pair-specific. The warnings a TS developer needs for Go are not the warnings a Python developer needs.
- Written as plain markdown, the playbook works in any agent — Claude Code, Cursor, whatever reads the file.
If you're switching stacks this year, try reframing your prompts: "explain this like I'm a TypeScript developer" is a far better prompt than "explain this to me". Hopefully it saves you the staring-at-:= moment I had.