Basics & Syntax
Go’s design philosophy
Section titled “Go’s design philosophy”Go was designed around four ideas that shape everything you write in it.
Simplicity over cleverness. The language has a small spec — just 25 keywords in total. There is deliberately no overloading, no default arguments, no implicit conversions, and no inheritance. When you read Go code written by someone else, it looks almost exactly like code you would have written yourself — that is intentional.
Fast compile. Fast compilation was a founding design goal, not an afterthought — Go was created at Google specifically to fix the slow, multi-minute C++ build times its authors were living with. Compiling even a large codebase typically takes seconds, which keeps the edit-compile-run feedback loop short and makes iteration fast.
Explicit over implicit. Type conversions and error handling are explicit — nothing happens behind the scenes that you have not written. Where a value lives, stack or heap, is decided by the compiler’s escape analysis, not by anything visible at the call site — see Pointers. This makes the code easy to trace and audit.
One obvious way. Go’s tooling enforces a single code style (gofmt), a single module system (go mod), and a single test runner (go test). Debates about formatting, import order, and project layout simply do not happen.
// The compiler rejects unused imports and unused local variables.// This keeps every file minimal and every dependency intentional.import "fmt"
func greet(name string) string { return "Hello, " + name}What a Go program becomes
Section titled “What a Go program becomes”go build compiles your program straight to a native machine-code binary for your GOOS/GOARCH — there is no bytecode and no virtual machine involved at runtime, unlike Java or the CPython interpreter. On this machine, go build on a one-file “hello world” program produces a self-contained executable a couple of megabytes in size (most of that is the Go runtime — the scheduler and garbage collector — linked directly into the binary), and running it needs nothing but the operating system: no Go installation, no separate runtime to ship alongside it.
go run is a convenience wrapper, not a different execution model: it compiles to a temporary binary in the background and immediately executes it, then discards the binary. It is the fastest way to try something while learning — every playground on this site behaves like go run — but for anything you deploy or benchmark repeatedly, go build once and run the resulting binary directly.
What this module covers
Section titled “What this module covers”| Lesson | Topics |
|---|---|
| Variables & Types | var, :=, zero values, const, iota, overflow, strings as bytes |
| Control Flow | if with init, for (three forms), labeled break/continue, switch |
| Functions | Multiple returns, defer, closures, first-class functions, pass-by-value |
| Packages & Imports | package, import, initialization order, internal/, go.mod |
By the end of this module you will be able to read and write complete idiomatic Go programs from scratch.
Your first Go program
Section titled “Your first Go program”Every executable Go program lives in package main and execution starts in func main. Here is a program that prints a greeting and shows a few basic values:
package main
import "fmt"
func main() { name := "Go" version := 1.27 fast := true fmt.Println("Hello from", name) fmt.Println("Version:", version) fmt.Println("Fast compile:", fast)}Run it below to see the output. Every concept used here — short variable declaration, basic types, fmt.Println — is covered in detail in the lessons that follow.
package main
import "fmt"
func main() { name := "Go" version := 1.27 fast := true fmt.Println("Hello from", name) fmt.Println("Version:", version) fmt.Println("Fast compile:", fast)
// Integer arithmetic is exact; no implicit float promotion a := 10 b := 3 fmt.Println("10 / 3 =", a/b) fmt.Println("10 % 3 =", a%b)} Trade-offs
Section titled “Trade-offs”| What you get | Benefit | Cost |
|---|---|---|
| Static typing | The compiler catches bugs before runtime | Slightly more code than a dynamic language |
| Only 25 keywords | Fast to learn, easy to read other people’s code | Some features (e.g. early generics support) need workarounds |
| Fast compile | Short feedback loop, fast iteration | No fully-featured REPL |
| Garbage collection | No manual memory management | Unpredictable GC pauses in latency-critical systems |
| Native binary, no VM | No runtime to install, fast startup, easy to deploy (one file) | Larger binary than an interpreted script; the Go runtime is always linked in |
Common Misconceptions
Section titled “Common Misconceptions”- “Go is just a modern C.” Go has garbage collection, goroutines, interfaces, and a module system — all fundamentally different from C.
- “Go has no OOP.” Go has structs, methods, and interfaces that support composition, but it has no class hierarchy like Java.
- “Go is only for backend servers.” Go is also used for CLI tools, DevOps tooling, WebAssembly, embedded systems, and data pipelines.
- “
go runis how Go programs are meant to be executed in production.”go runis a development convenience that compiles to a throwaway binary every time; deployed Go programs are built once withgo buildand shipped as that binary.
Reading AI-generated code
Section titled “Reading AI-generated code”Across every lesson in this module, the same handful of checks catch most generated-code bugs before they ship:
- Does a
:=inside anif/forreuse a name (oftenerr) that already exists in the enclosing scope, or does it silently declare a new, shadowed variable? - Does a
breaksit inside aswitch(orselect) that is itself inside afor? It only exits the inner construct, never the loop. - Does a
defersit inside a loop, or doesdefer f.Close()throw away an error that mattered? - Is a function value, or a struct field that holds one, called without a
nilcheck first?
🐛 Spot the bug
type Config struct { OnReady func()}
func main() { c := Config{} c.OnReady()}Show the answer
OnReady is a function-valued field that was never assigned, so it holds its zero value, nil. Calling a nil function value panics: runtime error: invalid memory address or nil pointer dereference. This is the same class of bug as a nil pointer dereference — the fix is to check if c.OnReady != nil { c.OnReady() } before calling it, or to give Config a constructor that always sets a default no-op function.