THE GO LEARNING LIBRARY
Your Go learning map.
Choose a direction, open a branch, and follow the lessons in order — or go straight to the topic you need.
The library includes the expanded 1.1.3 course, available here now. The current App Store app is 1.1.2; these newer architecture lessons are coming to the app.
Saved in this browser. Web progress is independent of app progress.
167 lessons in your learning map
Go foundations
From values and control flow to types, interfaces and packages.
Go coreDeclarations, zero values, multiple returns and the shapes Go repeats everywhere.0/11completed
- Nothing is uninitialisedPredict the value of any declared variable before it is assigned.
- := declares, = assignsChoose between := and = without guessing, and know when := is illegal.
- Read a declaration one scope at a timeFollow values through a short declaration, an assignment and a shadowed name without running the program.
- Read the failure path before the happy pathTrace a result and error through a complete boundary, including wrapping and partial results.
- Two returns instead of one exceptionWrite a function that returns a result and an error, and read one.
- An unused variable stops the buildRecognise the two unused errors on sight and know why they exist.
- Constants have no type until they need oneExplain why 1 << 40 compiles as a constant but overflows as an int32.
- switch does not fall throughWrite a Go switch without reaching for break.
- Go never converts a number for youFix a mismatched-type expression without guessing.
- A return can name what it is handing backWrite a short function that uses a naked return, and know when not to.
- Deferred calls stack, last one firstSay the print order of three deferred calls.
Types you defineStructs, methods, pointers, and functions that are values.0/5completed
- A struct is data, and its fields have namesDeclare a struct type and build values of it without positional guessing.
- A method is a function with a receiverPut a method on your own type and choose the receiver deliberately.
- A pointer is for changing something, not for speedSay when a pointer is required and when it is only noise.
- A function is a value, and it remembers where it came fromReturn a function that carries state, and know what it captured.
- Returning a pointer to a local is fineExplain why Go lets you return the address of a local variable.
Slices, maps and stringsLength against capacity, shared backing arrays, and why a string is not a []char.0/16completed
- A slice is a window, not a copied arrayPredict a subslice, an element write and append by tracking the backing array.
- len is what you have, cap is what you can grow intoPredict when append allocates and when it writes in place.
- Two slices, one arraySpot the moment a subslice starts sharing writes with its parent.
- Shared slices and concurrent appendSeparate shared headers, shared elements and safe collection of concurrent results.
- Reading a missing key is fine, writing to a nil map is notUse comma-ok, and know which map operation panics.
- A string is bytes; range gives you runesIndex, slice and iterate a string without corrupting characters.
- range works on functions tooWrite something that can be ranged over without building a slice first.
- len is what you may read; cap is what you may resliceSay which of two similar-looking index expressions panics.
- A tiny slice can keep a large buffer aliveRecognize retained backing arrays and detach small long-lived results deliberately.
- Map iteration order is not specifiedProduce stable output from a map without hoping.
- Concatenating in a loop repeatedly rebuilds a stringUse strings.Builder for repeated construction without rebuilding the prefix.
- make builds the header; new only allocatesPick make or new for a slice, a map and a pointer.
- The length is part of an array's typeWrite a function that takes [4]int and returns [3]int.
- Comma-ok is how you tell zero from missingReport whether a key is present when its value can be zero.
- A nil slice is empty, and append still worksPredict what len, == nil and append do to a zero slice.
- A function is a value you can passApply a func(int) int to every element of a slice.
InterfacesImplicit satisfaction, method sets, and the nil that is not nil.0/7completed
- The consumer declares the interfaceWrite the smallest interface a function needs, at the call site.
- Pointer receiver, pointer method setPredict whether a value or only a pointer satisfies an interface.
- A nil pointer in an interface is not a nil interfaceExplain why err != nil can be true when the error is nil.
- Asking an interface what it is holdingRead a value out of an interface without risking a panic.
- Embedding is not inheritancePredict which method runs when an embedded type has one too.
- fmt looks for String(), not a Stringer keywordMake a type print itself as a dotted IPv4 address.
- any is interface{}, and it has no methodsReport the dynamic type of a value held in any.
Generics, and when not toType parameters, constraints, and the times an interface is the better answer.0/5completed
- A type parameter is written in square bracketsWrite a function that works for several types without interface{} and without a cast.
- A constraint is an interface with types in itConstrain a type parameter to what your code actually needs.
- A type can take parameters tooWrite a container that holds one type rather than any type.
- A method can take its own type parameterWrite the method that could only be a function before Go 1.27.
- Most code that could be generic should not beRecognise the three cases where a type parameter earns its place, and the ones where it does not.
ErrorsWrapping, errors.Is and errors.As, sentinels, defer and the panic boundary.0/6completed
- %w adds context without hiding the causeWrap an error so callers can still match the original.
- Is compares, As extractsChoose between errors.Is and errors.As without trial and error.
- defer runs last in, first outRelease what you acquired, in the right order, on every path.
- An error is any type with an Error methodWrite an error that carries data the caller can read.
- panic is for bugs, not for failuresSay which of two situations deserves a panic.
- recover belongs at the boundaryTurn a panic into an error without swallowing the rest of the program.
Packages, modules and toolingImport paths, go.mod, go test and go vet, without a search engine.0/5completed
- The import path is the module path plus the directoryPredict the import path of any package in a module.
- A capital letter is the access modifierControl visibility without keywords, and name things accordingly.
- Tests are a file, not a frameworkWrite a table-driven test and run it locally.
- The package name is half the APIName a package and its exported function without repeating yourself.
- init runs before main, and order is definedPredict the order of package variables, init and main.
Concurrency & services
Coordinate work, understand the runtime, and build useful services.
ConcurrencyGoroutines, channel ownership, select, buffers and who may send.0/13completed
- An unbuffered channel is a meetingSay what blocks, and for how long, on a channel with no buffer.
- go returns nothingGet a result out of a goroutine, and know when the program stops waiting.
- Cancellation is a request; joining proves exitDesign a worker so it can stop waiting and its owner can wait for it to finish.
- The sender closes, the receiver rangesDecide who closes a channel, and why the other side must not.
- select waits on several things; context says stopMake a blocking operation cancellable without a flag or a kill switch.
- A mutex is for state; a channel is for handing overChoose between a mutex and a channel for a given problem.
- The worker pool, written onceBuild a fixed set of workers that finishes cleanly.
- More goroutines are not more capacityBound active workers and queued work according to the resource that actually limits throughput.
- A buffer is a mailbox, not a meetingSend n values on a channel of capacity n without another goroutine.
- range over a channel ends when it closesDrain a channel with range, and close it from the sender.
- default makes select try, not waitReceive from a channel if a value is ready, and return if it is not.
- The arrow in the type is who may sendWrite a producer that takes chan<- int and closes it.
- sync.Once runs a function one time, for everyoneInitialise shared state without a lock you have to remember.
Standard library patternsio.Reader and io.Writer, encoding/json, and testing without a framework.0/8completed
- Two interfaces, one method eachWrite a function that works on a file, a string and a test buffer.
- Struct tags are the JSON schemaControl field names, omit empties, and know what unmarshalling leaves untouched.
- A Duration is a number of nanoseconds with a typeWrite a timeout without wondering what unit it is in.
- Sorting by whatever you likeSort a slice of structs by two keys.
- Every blocking call should take a contextMake a function that waits stop waiting when told to.
- http.Handler is one method, like ReaderServe /hello from an in-memory request, with no listen.
- strconv is how text becomes a numberParse a port with Atoi and reject anything outside 1…65535.
- image.Image is three methods, not a fileImplement a solid-colour image the standard library will accept.
Runtime and memoryStacks, GC, goroutines, threads and diagnostic evidence.0/5completed
- Stacks, escape analysis and object lifetimesExplain why returning a local address is safe and why allocation placement must be measured.
- What the garbage collector can and cannot freeFollow references from roots and distinguish unreachable objects from unwanted live data.
- GC cost, allocation rate and memory budgetsDistinguish reducing live data from tuning collection frequency and respect a container memory limit.
- Goroutines, OS threads and GOMAXPROCSExplain concurrency versus parallel execution without assuming a goroutine is an OS thread.
- Find a leak by following evidenceChoose a heap profile, goroutine profile, CPU profile or trace to test a specific hypothesis.
Go servicesHTTP, Gin, Redis and the capacity of downstream services.0/5completed
- An HTTP request has a lifecycle and a budgetSeparate request parsing, domain work, downstream deadlines and response writing.
- Gin routes are adapters, not the whole applicationRead route groups, path parameters and injected dependencies in a small Gin API.
- Validate a request before doing side effectsFollow JSON binding, size limits, validation and a safe error response.
- Redis is fast; its capacity is still finiteDistinguish client pool limits, request deadlines, rate limits and bounded command batches.
- Cache misses, retries and overload amplificationDesign a cache-miss path that avoids multiplying identical work and ambiguous retries.
Architecture & clean code
Choose boundaries deliberately. Keep the implementation clear.
Go architecturesCompare monoliths, layers, hexagonal and clean designs, DDD, CQRS, events and distributed services.0/19completed
- Packages follow responsibilities, not a diagramChoose cohesive package boundaries and keep concrete dependencies at the edges.
- Ports and adapters with a real requestTrace an HTTP adapter through an application operation to a storage port and compare test boundaries.
- Choose a style; keep the code easy to changeCompare a modular monolith, layered design and microservices using concrete operational tradeoffs.
- Simple monolith: start with one executableKeep a small application understandable without premature service boundaries.
- Layered architecture: transport, application, storageSeparate responsibilities without confusing logical layers with deployed tiers.
- Modular monolith: boundaries inside one deploymentGive business capabilities explicit APIs while retaining local transactions.
- Clean and Onion: dependencies point toward policyKeep domain policy independent of framework and database details.
- Vertical slices: organize around a use caseKeep feature changes together without duplicating shared invariants.
- DDD: bounded contexts and aggregatesModel language and invariants before choosing storage or transport.
- CQRS: different models for commands and queriesSeparate change rules from read shape without assuming two databases.
- Event sourcing: state from a durable historyDistinguish a source of truth from ordinary logs and notifications.
- Event-driven services: delivery and idempotencyTreat duplicate delivery as normal and define the durable boundary.
- Microservices: pay for independent ownershipExtract a remote boundary only when its independence is valuable.
- SOA and RPC: stable service contractsCompare service-oriented integration with smaller independently owned services.
- BFF: an API shaped for one clientCompose client-specific views while retaining business ownership.
- Serverless handlers: bounded work and retry-safe effectsSeparate invocation mechanics from deterministic application behavior.
- Pipes and filters: data through independent stagesCompose transformations while making backpressure and errors explicit.
- Microkernel: a small core with explicit extensionsExtend behavior through contracts without assuming dynamic shared libraries.
- Web, queue, worker: make slow work durableSeparate request latency from background completion.
Clean code in GoNames, interfaces, errors, ownership, tests and safe refactoring.0/8completed
- Names that expose responsibilityMake a caller understand units, results and ownership from the API.
- Small interfaces belong near the consumerDescribe a real substitution boundary without wrapping every concrete type.
- Errors: preserve cause and choose one reporting boundaryKeep failure context useful without logging the same failure at every layer.
- Ownership: copy mutable data deliberatelyMake it clear whether a caller lends, shares or transfers a buffer.
- Configuration: validate once at constructionTurn invalid setup into an explicit startup error.
- Behavior tests: tables and boundary casesTest an observable contract with success and failure inputs.
- Refactoring: make the safe change smallSeparate a behavior-preserving edit from a new requirement.
- SOLID in Go: principles without class scaffoldingTranslate responsibility, substitution and dependency inversion into Go contracts.
Design patterns
Creation, composition, behavior and the patterns that fit concurrent Go.
Patterns: creationFactory Method, Abstract Factory, Builder, Prototype and Singleton.0/5completed
- Factory Method: supply the creation stepVary product creation without duplicating the algorithm that uses it.
- Abstract Factory: compatible product familiesCreate related products through one selected family.
- Builder: assemble then validateRepresent staged construction without making invalid results usable.
- Prototype: copy a useful starting valueChoose shallow or deep copying according to ownership.
- Singleton: once does not mean global mutable stateUnderstand one-instance intent and choose an explicit lifetime.
Patterns: compositionAdapter, Bridge, Composite, Decorator, Facade, Flyweight and Proxy.0/7completed
- Adapter: translate one contract into anotherKeep conversion details at the boundary that needs them.
- Bridge: two independent dimensions of variationCompose an abstraction with a separately variable implementation.
- Composite: treat leaves and groups uniformlyDefine recursive behavior while guarding the shape of the structure.
- Decorator: add behavior while preserving the contractWrap a small operation instead of constructing a type hierarchy.
- Facade: one useful entry point over a subsystemSimplify orchestration without pretending several effects are atomic.
- Flyweight: share immutable intrinsic stateSeparate reusable data from per-use context before optimizing memory.
- Proxy: control access to an existing operationMake caching or lazy access preserve the caller’s expectations.
Patterns: behaviorEleven classic patterns, from Strategy and Command to State, Visitor and Interpreter.0/11completed
- Strategy: choose an algorithm explicitlyPass interchangeable behavior through a function or small interface.
- Command: represent an action as a valueMake actions queueable or reversible without claiming every effect can be undone.
- Chain of Responsibility: ordered handlersStop or delegate deliberately and make short-circuit order visible.
- Iterator: yield values without exposing storageUse iter.Seq or a small Next API and honor early termination.
- Mediator: centralize collaboration deliberatelyLet participants communicate through a focused coordinator.
- Memento: retain a restorable snapshotLet the owner define what a valid snapshot contains.
- Observer: subscribe, notify and unsubscribeKeep notification lifetime and delivery semantics explicit.
- State: behavior follows a valid lifecycleRepresent legal transitions instead of scattered boolean combinations.
- Template Method: fixed workflow, supplied stepsAdapt the inheritance-based intent using composition in Go.
- Visitor: new operations over a stable set of nodesUnderstand the tradeoff between adding node types and adding operations.
- Interpreter: evaluate a deliberately small languageModel a grammar without treating arbitrary text as executable code.
Patterns: concurrent GoWorker pools, fan-in, cancellation, ownership, retries and circuit breakers.0/6completed
- Worker pool: bound simultaneous workControl active work instead of starting one goroutine for every input.
- Fan-out and fan-in: merge without assuming orderCoordinate multiple producers and one result stream.
- Cancellation: propagate an exit path through every handoffMake a producer stop when its consumer no longer needs results.
- Single owner: serialize state through messagesUse one goroutine to own mutable state and define request completion.
- Retry: bounded attempts and idempotent effectsRetry transient failures within one deadline without multiplying side effects.
- Circuit breaker: stop calls while a dependency recoversSeparate retry policy from failure isolation and a controlled recovery probe.
Around Go
The command line, containers and Kubernetes. Real commands, one layer at a time.
Around Go: command lineBash, Git, Go tools, HTTP debugging and Linux service commands.0/11completed
- Bash essentials: navigate, inspect and copy filesUse pwd, cd, ls, mkdir, cp and mv in a small disposable workspace.
- Find code and inspect logs from the command lineCombine find, grep, head, tail, sort and uniq without hiding their exit behavior.
- Bash passes arguments; quoting defines their boundariesRead a small build script without splitting paths or accidentally invoking a shell through Go.
- Pipelines, exit codes and explicit failure handlingRead pipeline failure behavior and keep configuration and secrets out of command strings.
- Processes, signals and exit statusStart one Go service, retain its PID, send TERM and wait for its result.
- Small scripts: arguments, environment and cleanupRead required configuration, forward quoted arguments and clean a temporary workspace with a trap.
- Git daily work: inspect, branch, stage and commitKeep working tree, staging area and committed history distinct.
- Git collaboration: fetch, compare, stash and pushInspect upstream changes before integration and keep unfinished work recoverable.
- Go toolbelt: modules, tests, vet and buildUse the native Go CLI to inspect a module and check a service before packaging it.
- HTTP debugging: curl, status codes and time budgetsProbe a local API and separate DNS, connection, HTTP and application failures.
- A Go service on Linux: systemctl and journalctlInspect a systemd service, follow its logs and understand its restart boundary.
Around Go: containersBuild, run, inspect and publish with Docker, Compose and Podman.0/6completed
- Build a small Go image with explicit runtime needsSeparate the build stage from the runtime image and identify what a minimal image leaves out.
- Docker daily commands: build, run, inspect and stopRun a named local container and diagnose it with logs, inspect and stats.
- Compose: run an API and Redis as one local stackRead compose.yaml and use config, up, ps, logs, exec and down.
- Container storage: bind mounts, volumes and permissionsChoose a mount for configuration or persistent state and inspect it without deleting data.
- Docker and Podman: images are not running containersDistinguish image, container, host networking and persistent data while comparing local runtimes.
- Publish an image: tags, digests and target platformsTag and push an image you own, inspect its digest and match its platform to the deployment.
Around Go: KubernetesContexts, deployments, logs, services, configuration, probes and rollbacks.0/8completed
- kubectl first steps: context, namespace and permissionsInspect the target cluster and explicitly scope commands before making changes.
- Kubernetes reconciles a workload; a Service routes to itRead Deployment, Pod labels, resource budgets and a Service without confusing them with an application build.
- Deploy a manifest: explain, dry-run, diff and applyValidate a workload document and observe reconciliation rather than assuming apply means ready.
- Debug Pods: get, describe, logs, events and topFind the concrete reason behind Pending, CrashLoopBackOff or an unready container.
- Service routing: selectors, endpoints and port-forwardTrace a request from a local port through a Service to a ready application endpoint.
- Configuration: ConfigMaps, Secrets and update behaviorCreate a non-secret setting, mount a Secret reference and understand why environment changes need replacement.
- Readiness, liveness and graceful shutdownGive each health signal one purpose and arrange a bounded stop that drains owned work.
- Release operations: rollout, scale and rollbackChange an image deliberately, observe revisions and understand what rollback cannot restore.
Open any branch and choose a lesson. The map suggests an order; every lesson is freely accessible.