Unit 2 · Software engineering · Lesson 11 of 15
Go essentials
Step 1: Learn the ideas
- Slices: length, capacity, append and aliasing
- Maps: nil maps, iteration order, concurrency
- Interfaces, implicit satisfaction and nil interface values
- Errors as values; wrapping, errors.Is and errors.As
- Goroutines and goroutine leaks
- Channels: buffering, closing, nil channels
- select, timeouts and context cancellation
- Memory model and the race detector
Why it matters
Go makes concurrency cheap to start and easy to get subtly wrong. In interviews conducted in Go, the recurring question is whether you can stop what you start: every goroutine needs an exit path, every request a deadline, every shared variable an owner or a lock. The other common bug class comes from value semantics: copying a slice copies a view, not the data.
Core ideas
- Slices are a (pointer, length, capacity) header over an array. Slicing shares the backing array, and
appendwrites in place when capacity allows and reallocates otherwise, so two slices cut from one array can overwrite each other's elements. Copy explicitly (slices.Clone,copy) or cap the capacity with a full slice expressions[lo:hi:hi]before handing out a sub-slice. - Maps refer to a runtime hash table. Reading a nil map returns zero values; writing to one panics. Iteration order is unspecified and varies between runs. Maps are unsafe when any goroutine writes concurrently; guard them with a mutex.
- Interfaces are satisfied implicitly by method sets. An interface value pairs a dynamic type with a value and is nil only when both are, so returning a nil
*MyErroras anerrorproduces a non-nil error. Keep interfaces small and define them where they are used. - Errors are ordinary return values. Add context with
fmt.Errorf("load config: %w", err)and inspect witherrors.Is(a sentinel) orerrors.As(a type), never by matching message strings. Reservepanicfor programming errors. - Goroutines and channels. A send on an unbuffered channel completes only when a receiver takes the value, synchronizing both sides; a buffered channel decouples them up to its capacity. Only the sender should close a channel. Receiving from a closed channel yields the zero value with
ok == false, sending on one panics, and a nil channel blocks forever, which is useful for disabling aselectcase. A goroutine blocked forever is a leak the runtime never reclaims. - select and context.
selectwaits on several channel operations and picks pseudo-randomly among the ready ones; adefaultcase makes it non-blocking. Pass acontext.Contextas the first parameter of request-scoped functions, watchctx.Done()in loops and selects, and always call the cancel function returned bycontext.WithCancelorcontext.WithTimeout. - Memory model and races. Channel operations, mutexes and
sync/atomiccreate happens-before edges. Without them, concurrent access where one side writes is a data race, and races on multiword values such as slices, strings, maps and interfaces can corrupt memory rather than merely return stale data.go test -racereports races that occur during the run.
Since Go 1.22, modules declaring Go 1.22 or later get a fresh loop variable per iteration, removing the classic bug of goroutines all capturing the final value.
Common misconceptions
- "Passing a slice copies its data." It copies the header; the callee can modify the caller's elements, and whether its appends are visible depends on capacity.
- "A nil pointer returned as an error is a nil error." The interface holds a non-nil type, so
err != nilis true. - "The race detector proves there are no races." It only finds races on code paths that execute during the run.
- "Channels must be closed to be freed." Unreachable channels are garbage-collected; close one only to tell receivers that no more values will come.
How to approach it
Work through A Tour of Go, then write a pipeline that stops cleanly when its context is cancelled and a worker pool with a fixed number of goroutines. Run both under -race, remove a lock on purpose and read the report. Then extend them with timeouts, error propagation and graceful shutdown.
Suggested order
- 1Work through A Tour of Go (Basics, Methods and interfaces, Generics, Concurrency) and do the exercises; skim anything you already know.
- 2Read "Go Slices: usage and internals" and "Go maps in action"; write a test that shows append aliasing between two sub-slices and fix it with a full slice expression.
- 3Read "Working with Errors in Go 1.13" and the Errors section of Effective Go; refactor a small function to wrap errors with %w and test them with errors.Is and errors.As.
- 4Read the Concurrency section of Effective Go and "Go Concurrency Patterns: Pipelines and cancellation"; build a three-stage pipeline that can stop early without leaking goroutines.
- 5Read "Go Concurrency Patterns: Context"; add a per-request timeout to the pipeline and make every stage exit on ctx.Done().
- 6Read the advice and informal overview of The Go Memory Model and the "Typical Data Races" section of the race detector article; introduce a race into a worker pool, find it with go test -race and fix it.
- 7Optional, larger: MIT 6.5840 Lab 1 (MapReduce) is written in Go and exercises goroutines, RPC, locking and the race detector.
Step 2: Check off what you can do
Tap each one you can genuinely do or explain. Each earns 10 XP.
Step 3: Go deeper
A Tour of GoStart here
The Go Project (go.dev)Section: Methods and interfaces; Concurrency
Language fluency with runnable examples; do every exercise in the Methods and interfaces and Concurrency modules.
Open(opens in a new tab)Link responded, 5 Oct 2026The Go Blog (selected articles)
The Go Project (go.dev)Section: Go Slices: usage and internals
Slice headers, capacity and append; the maps, errors, context, pipelines and loop-variable articles are listed under this resource.
Open(opens in a new tab)Link responded, 5 Oct 2026Effective Go
The Go Project (go.dev)Section: Concurrency
Idiomatic goroutines and channels, plus the Interfaces and Errors sections; it predates generics and modules.
Open(opens in a new tab)Link responded, 5 Oct 2026The Go Memory ModelStart here
The Go Project (go.dev)Section: Informal Overview
The happens-before guarantees of channels, mutexes and atomics, and why races on multiword values can corrupt memory.
Open(opens in a new tab)Link responded, 5 Oct 2026Data Race Detector (Go documentation)
The Go Project (go.dev)Section: Typical Data Races
Running go test -race and recognizing the common race shapes it reports.
Open(opens in a new tab)Link responded, 5 Oct 2026
Step 4: Practice
Do the Tour exercises first; Equivalent Binary Trees and Web Crawler are the concurrency ones, and the crawler should end up with a mutex-guarded visited map and a clean termination condition. Then solve three LeetCode problems in Go to practice maps and slices: Group Anagrams with a map from a canonical key to slices, Time Based Key-Value Store with sort.Search over per-key slices, and LRU Cache with container/list plus a map. Outside LeetCode, write a worker pool that processes a channel of jobs with a fixed number of goroutines, stops on context cancellation and returns the first error, and run its tests with -race. The 6.5840 MapReduce lab is the larger follow-up.
- A Tour of Go exercises (slices, maps, closures, Stringers, errors, readers, binary trees, web crawler)Open(opens in a new tab)Not attemptedThe Go Project
- Group AnagramsLeetCode(opens in a new tab)Not attemptedLeetCode
- Time Based Key-Value StoreLeetCode(opens in a new tab)Not attemptedLeetCode
- LRU CacheLeetCode(opens in a new tab)Not attemptedLeetCode
- MIT 6.5840 Lab 1: MapReduceOpen(opens in a new tab)Not attemptedMIT 6.5840
Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).
Step 5: Test yourself
Answer each one out loud or on paper before you finish the lesson.
- Question 1When does append modify the array that another slice also sees?
- Question 2What happens when you write to a nil map? Read from one?
- Question 3How does errors.As differ from errors.Is?
- Question 4Who should close a channel, and what happens on a send after close?
- Question 5How do you stop a goroutine that is blocked receiving from a channel?
- Question 6What changed about loop variables in Go 1.22?
You’re done when you can
- Predict the output of an append-aliasing snippet and fix it two different ways.
- Explain why a function returning a nil *MyError as error yields a non-nil error, and fix it.
- Write a cancellable worker pool from scratch in under 30 minutes that passes go test -race.
- Explain which operations create happens-before edges in Go and what a data race on a slice can do.


