Go (Golang)
Go is what teams reach for when they want fast, simple network services that ship as a single static binary: concurrency built into the language, a collector tuned for very short pauses, and a standard library that already speaks HTTP and JSON. Interviewers probe it hard because the language is small enough that the interesting questions are about semantics and runtime behavior rather than syntax.
on this pageshowhide
guide
overview
~2 minGo interviews test whether you know what the language does underneath its small syntax. The grammar is quick to learn, so the opening questions go straight to semantics: what assigning a slice or a struct actually copies, which methods a pointer carries that a plain value does not, why an interface holding a nil pointer is not itself nil. The longest stretch is usually concurrency — goroutines, channels, `select`, the `sync` package and `context` — because a Go hire is expected to write a worker that is bounded, cancellable and free of races with nothing but the standard library. Errors come next, since returning them as values makes failure handling part of every function's design. Senior rounds move below the language to the scheduler, the garbage collector and escape analysis, and then out to production: reading a profile, sizing memory and CPU inside a container, stopping a server without dropping requests. The hub follows those seams. The language itself is [Core Language and Type System](/topics/lang-go-core-language), [Errors and Failure Design](/topics/lang-go-errors) and [Idioms and API Design](/topics/lang-go-idioms). [Concurrency](/topics/lang-go-concurrency) has its own section, and [Runtime Machinery](/topics/lang-go-runtime-internals) explains what the scheduler and collector do beneath it. The standard library splits by job: [Text, Time and Collections](/topics/lang-go-values), [Operating System Access](/topics/lang-go-os-io), [Data Encoding and Storage](/topics/lang-go-data-encoding), [Network Services and Clients](/topics/lang-go-http-services) and [Security and Cryptography](/topics/lang-go-security). The working-engineer layer is [Automated Testing](/topics/lang-go-testing), [Modules, Build and Toolchain](/topics/lang-go-tooling-ecosystem), [Performance and Diagnostics](/topics/lang-go-performance) and [Service Telemetry](/topics/lang-go-telemetry), and the escape hatches — reflection, `unsafe`, cgo — sit in [Low-Level and Foreign Code](/topics/lang-go-interop). Learn the core language and errors first: nearly every later answer assumes you can say what a copy shares and how a failure travels back up the call stack. Concurrency comes next, then the HTTP section, which is where most Go roles spend their day and where goroutines, contexts and errors meet in one handler. Pick up testing and the toolchain alongside, because interviewers expect you to reach for the built-in tools rather than name a third-party one. Leave runtime internals and performance for last unless the role is senior or infrastructure-facing; they only make sense once you know which behaviour they explain.
primer
### Everything is passed by value — know what the value holds Assignment, argument passing and `range` all copy. What the copy shares depends on the type: an array or a struct copies its contents, shallowly, while a slice, map, channel, function or interface is a small descriptor that refers to data stored elsewhere, so two copies see the same elements. A large share of junior and mid-level questions — surprising `append` results, methods whose writes seem to vanish, a map mutated through a "copy" — reduce to this one rule. ### Zero values are meant to be usable Every variable starts at its type's zero value, and idiomatic types are designed so that value works: an empty `bytes.Buffer`, an unlocked `sync.Mutex`, a nil slice you can append to. Remember the exceptions, because interviewers do: writing into a nil map panics, and sending or receiving on a nil channel blocks forever. A constructor exists when the zero value cannot be made valid, not out of habit. ### Interfaces are satisfied implicitly and kept small A type satisfies an interface by having its methods; there is no `implements` clause. That lets the consumer own the interface: a package declares the two or three methods it needs, and any type with them fits, including types written before the interface existed. The standard library's one-method interfaces — `io.Reader`, `io.Writer`, `error`, `http.Handler` — are the model to imitate. Method sets decide what fits, and a method declared on a pointer receiver belongs to `*T`, not to `T`. ### Errors are values, and the caller decides A function that can fail hands back an `error` alongside its result, and the caller checks it before trusting anything else. Wrapping adds context on the way up while keeping the original inspectable, so a caller can still match a sentinel or an error type several layers down. `panic` is for programmer mistakes and states the program cannot continue from; using it as a general exception mechanism is a design interviewers expect you to reject. ### Concurrency is cheap to start and your job to stop A goroutine starts with a stack of a few kilobytes, so launching one is easy; knowing how it ends is the real skill. Channels hand data from one goroutine to another, mutexes guard shared state, and `context.Context` carries cancellation and deadlines down the call tree. Nothing stops a goroutine from outside: each one has to watch for its own exit signal, and one that blocks forever is a leak that usually goes unnoticed until memory climbs. The race detector finds unsynchronised access, but only on paths a test or a run actually executed. ### The runtime is part of your program Every binary carries a scheduler that multiplexes goroutines onto OS threads and a concurrent, non-moving garbage collector designed for short pauses. The compiler's escape analysis decides whether a value lives on the stack or the heap, so allocation is a property of how the code is shaped rather than of `new` or `&`. Senior questions ask you to connect a symptom — latency spikes, steady memory growth, idle CPUs under load — to one of these mechanisms, and then to the setting that adjusts it: `GOGC`, `GOMEMLIMIT` or `GOMAXPROCS`. ### Simplicity is a design position Go leaves out inheritance, exceptions and operator overloading, went without generics until 1.18, and ships one formatter and one build command. Interviewers probe whether you can defend those choices and write code that follows the conventions they produce: short names in small scopes, composition through embedding, accepting interfaces and returning concrete types, a small exported surface. An abstraction that fights the language tends to read as unfamiliarity with it. ### The standard library is the default dependency HTTP servers and clients, JSON, SQL access, TLS, structured logging, testing and profiling all ship with the toolchain, and interviews assume you know them. Expect to be asked what `net/http` or `database/sql` does for you and what it leaves to you — timeouts, pool sizes, closing bodies and rows — more often than which third-party package you would install.
- Goroutine
- A function running concurrently under the Go runtime's scheduler, started with the go statement; far cheaper than an OS thread and with no handle the caller can use to stop it.
- Channel
- A typed conduit that goroutines send values into and receive values from; unbuffered channels synchronise sender and receiver, buffered ones decouple them up to a fixed capacity.
- select statement
- A statement that waits on several channel operations at once and proceeds with whichever becomes ready; a default case makes it non-blocking.
- Zero value
- The value a variable holds before anything is assigned: 0, false, empty string, or nil for pointers, slices, maps, channels, functions and interfaces.
- Slice
- A three-part value of pointer, length and capacity describing a window into an underlying array; copying the slice copies the description, not the elements.
- Method set
- The methods callable through a given type; for a pointer type it includes methods with value receivers as well as pointer receivers, which decides interface satisfaction.
- Interface value
- A pair of dynamic type and dynamic value; it compares equal to nil only when both halves are empty, not merely when the stored pointer is nil.
- Embedding
- Declaring a field by type alone so the outer type gains the inner type's fields and methods; composition with promoted members, not inheritance.
- Type parameter
- A placeholder type in a generic function or type, restricted by a constraint interface that lists the methods or underlying types it allows.
- Sentinel error
- A package-level error variable, such as io.EOF, that callers compare against to recognise one specific condition.
- Error wrapping
- Building a new error that carries an earlier one inside it, so errors.Is and errors.As can search the chain while the message gains context.
- Context
- The context.Context value passed first through a call chain, carrying a cancellation signal, an optional deadline and a few request-scoped values.
- Data race
- Two goroutines touching the same memory at once, at least one writing, with no synchronisation between them; the result is undefined, not merely stale.
- Happens-before
- The ordering relation in Go's memory model that decides when a write in one goroutine is guaranteed visible to a read in another.
- Race detector
- Instrumentation enabled with the race flag on build and test commands that reports conflicting unsynchronised accesses when they actually occur during a run.
- GMP model
- The scheduler's structure: goroutines (G) run on OS threads (M), which must hold a processor (P); the number of Ps is set by GOMAXPROCS.
- Escape analysis
- The compiler pass that decides whether a variable can stay in its function's stack frame or must move to the garbage-collected heap.
- GOGC
- The garbage collector's growth setting: how much the heap may grow, as a percentage of live data, before the next cycle starts. The default is 100.
- GOMEMLIMIT
- A soft memory limit for the Go runtime, added in 1.19, that makes the collector work harder as total memory approaches the given size.
- Module
- A versioned collection of packages defined by a go.mod file, which names its path, its Go language version and its requirements.
- Minimal version selection
- Go's dependency rule: build with the lowest version of each module that satisfies every requirement in the graph, so builds are reproducible without a solver.
- Table-driven test
- A test that loops over a slice of named cases and runs each as a subtest, the standard shape for Go unit tests.
- pprof
- The profiling format and tool for Go: CPU, heap, goroutine, block and mutex profiles collected from the runtime and explored with go tool pprof.
- cgo
- The mechanism for calling C from Go and back; it needs a C toolchain, gives up the pure-Go static build and makes each crossing slower than a Go call.
The sections stack around a small core. Value semantics, method sets and interfaces from the core language are what every other section is written in. [Interfaces and Implicit Satisfaction](/topics/lang-go-core-language-interfaces) explains why a file, a network connection and a compressed stream can all be handed to the same function, and much of [Operating System Access](/topics/lang-go-os-io) and [Data Encoding and Storage](/topics/lang-go-data-encoding) is that one idea applied to readers and writers. Errors follow the same pattern: `error` is itself an interface, so [Wrapping and Inspection](/topics/lang-go-errors-wrapping) rests on the same dynamic type checks as any other interface value. Concurrency is where the language meets the runtime. Goroutines and channels are language features; what makes them cheap, and what makes them misbehave under load, lives in the [Goroutine Scheduler (GMP)](/topics/lang-go-runtime-internals-scheduler) and the [Garbage Collector](/topics/lang-go-runtime-internals-gc). [Performance and Diagnostics](/topics/lang-go-performance) is the same machinery seen from outside — profiles and traces are the runtime reporting on itself — so a tuning question is often a runtime question in disguise. `context.Context` is the thread through the service-facing sections. A server gives each request a context; handlers pass it to database calls, outbound requests and log records; cancellation then travels from a client that hung up down to the query it started. [Network Services and Clients](/topics/lang-go-http-services), [Relational Access](/topics/lang-go-data-encoding-sql), [Distributed Tracing Hooks](/topics/lang-go-telemetry-tracing) and [Termination Handling](/topics/lang-go-os-io-lifecycle) all depend on it, which is why a function that quietly drops its caller's context counts as a defect in review. Tooling and testing surround everything. The module system fixes which code builds, `go vet` and the race detector catch what the compiler accepts, and the testing package supplies the tables, benchmarks and fuzz targets that prove the rest. The escape hatches in [Low-Level and Foreign Code](/topics/lang-go-interop) are best understood by what each costs: reflection trades away compile-time type checks, `unsafe` trades away memory safety, and cgo trades away the pure-Go static build and cheap calls. A short function where several of these meet: ```go func fetchAll(ctx context.Context, urls []string) ([]Result, error) { ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() results := make(chan Result, len(urls)) for _, u := range urls { go func() { results <- fetch(ctx, u) }() } out := make([]Result, 0, len(urls)) for range urls { select { case r := <-results: out = append(out, r) case <-ctx.Done(): return out, fmt.Errorf("fetch all: %w", ctx.Err()) } } return out, nil } ``` The channel has a slot for every result, so goroutines still running after an early return can complete their send and exit instead of leaking. `select` lets the deadline win over a slow fetch, `%w` keeps the context's error matchable with `errors.Is` further up, and since Go 1.22 each iteration declares a fresh `u`, so the closures no longer share one variable. The design assumes `fetch` honours its context: if it ignores it, the timeout returns control to the caller while the work carries on in the background.
- Core Language and Type System →
Value copies, method sets, interfaces and slices are assumed by every other section; most junior questions start here.
- Errors and Failure Design →
Error values, wrapping and the panic boundary appear in almost every function you write or review, so learn them early.
- Concurrency →
The longest stretch of most rounds: goroutine lifetimes, channels and select, the sync package and context cancellation.
- Network Services and Clients →
Where most Go jobs live, and where concurrency, context and error handling meet in real handlers and clients.
- Automated Testing →
Interviewers expect the built-in testing package: table-driven cases, test doubles through interfaces, benchmarks and the race detector.
- Runtime Machinery →
Once the language feels routine, learn the scheduler, collector and memory layout that senior questions and performance work rest on.
Calling
appendwithout keeping its result, or keeping two slices over one array: a later append can overwrite elements the other slice still reads. See Slices, Maps and Structs.Returning a typed nil pointer as an
error: the interface still holds a type, soerr != nilis true and the caller sees a failure that never happened.Starting a goroutine with no way to end it: without a context, a closed channel or a done signal it can block forever, and leaked goroutines hold memory silently.
Treating a data race as harmless because tests passed: racy programs get no useful guarantee, and the race detector sees only the paths that ran.
Writing to a plain map from several goroutines without a lock: the runtime may detect it and abort the whole process with a fatal error that
recovercannot stop.Using
panicandrecoveras exceptions for ordinary failures: reviewers expect error returns, andrecoveronly works inside a deferred call on the panicking goroutine.Matching errors with
==or by message text: once any layer wraps with%w, equality stops finding the cause, whileerrors.Isanderrors.Asstill reach it.Shipping an
http.Serverorhttp.Clientwith its zero-value timeouts: a slow or silent peer can then hold a connection and a goroutine indefinitely.Storing a
context.Contextin a struct field or passing nil: pass it explicitly as the first argument, and usecontext.TODO()while the right one is unclear.Declaring an interface beside its only implementation just to mock it: Go convention lets the consumer define the small interface it needs.
Running in a memory-limited container without
GOMEMLIMIT: the collector paces itself on heap growth, not on the container limit, so the process can be killed for exceeding it.Deferring a
Closeinside a loop: deferred calls run when the function returns, so every file or row set stays open until the loop's function exits.
This guide assumes Go 1.25. Go keeps a strong compatibility promise, so most answers read the same on releases a few years older, but interviewers still ask about the milestones that changed everyday code: - **1.11 and 1.16** — modules arrived in 1.11 and became the default mode in 1.16, ending the GOPATH workflow. - **1.13** — `errors.Is`, `errors.As` and the `%w` verb made wrapping part of the standard library. - **1.14** — the scheduler gained asynchronous preemption, so a tight loop without function calls can no longer stall other goroutines or the collector. - **1.18** — generics, fuzzing in `go test`, and workspaces. - **1.19** — the soft memory limit, `GOMEMLIMIT`, and a revised memory model document. - **1.21** — `log/slog`, the `slices`, `maps` and `cmp` packages, the `min`, `max` and `clear` built-ins, and the `toolchain` line in go.mod. - **1.22** — loop variables became per-iteration, `range` over integers, and method and wildcard patterns in the standard `ServeMux`. - **1.23** — range-over-function iterators and the `iter` package. - **1.25** — `GOMAXPROCS` began respecting a container's CPU limit on Linux, and `testing/synctest` became stable. The loop-variable change is the one that still confuses candidates reading older code: ```go for _, v := range values { v := v // needed before 1.22: otherwise every goroutine shared one v go use(v) } ``` From 1.22 in a module that declares that version or later, each iteration already has its own `v`, and the copy line is redundant.
Go was created at Google and is developed as an open-source project. Interviewers expect you to place it in three settings. In **cloud infrastructure**, it is the language of much of the container and orchestration tooling — Docker, Kubernetes, Terraform and Prometheus are written in it — so platform and SRE roles often read and extend Go code before they write any. In **backend services**, it competes with Java and Kotlin on the JVM, with Node.js, and with Python: a single static binary, fast startup and a modest memory footprint against larger ecosystems and heavier frameworks. For **command-line tools**, cross-compiling a dependency-free binary for each platform is a common reason to choose it. The comparison that comes up most at senior level is with **Rust**: both compile to native code, but Go keeps a garbage collector and a deliberately small language, while Rust removes the collector and enforces memory safety at compile time at the cost of a steeper language. A good answer names that trade-off without ranking them. Around the standard library, expect to recognise a few common third-party names: routers such as chi or gin, the pgx driver for PostgreSQL, testify for assertions, gRPC with Protocol Buffers for service-to-service calls, and `golang.org/x/sync` for `errgroup`. Interviewers rarely test them in depth; they want to know you can tell when the standard library is enough.
explore
- Core Language and Type System189 questions
- Types, Values and Constants29 questions
- Slices, Maps and Structs24 questions
- Structs, Methods and Embedding25 questions
- Interfaces and Implicit Satisfaction27 questions
- Generics and Type Parameters25 questions
- Functions, Closures and Control Flow25 questions
- Packages and Visibility22 questions
- Identity and Assignability12 questions
- Concurrency172 questions
- Goroutine Lifecycle17 questions
- Channels and select29 questions
- The sync and sync/atomic Packages35 questions
- The context Package20 questions
- Coordination Patterns35 questions
- Races, Visibility and Detection20 questions
- Daemons and Background Work16 questions
- Runtime Machinery136 questions
- Goroutine Scheduler (GMP)35 questions
- Memory Allocation and Stacks16 questions
- Garbage Collector26 questions
- Value Representation21 questions
- Interface Representation13 questions
- Deferred Calls and Panics12 questions
- Program Startup and Exit13 questions
- Errors and Failure Design81 questions
- Values, Not Exceptions19 questions
- Sentinels, Types and Matching16 questions
- Wrapping and Inspection16 questions
- Panic Policy and Boundaries17 questions
- Standard Library Contracts13 questions
- Automated Testing102 questions
- Authoring and Fixtures33 questions
- Doubles and Seams17 questions
- Run Control23 questions
- Benchmarks and Fuzzing17 questions
- Removing Nondeterminism12 questions
- Modules, Build and Toolchain114 questions
- Dependency Resolution43 questions
- The go Command30 questions
- Shipping Binaries13 questions
- Analyzers and AST Rewriting9 questions
- Language Version Policy19 questions
- Performance and Diagnostics102 questions
- Profiles and pprof25 questions
- What the Tracer Shows12 questions
- Symptoms in Production22 questions
- Debugging a Live Process16 questions
- Tuning Under Container Limits15 questions
- Optimising with Evidence12 questions
- Text, Time and Collections129 questions
- String and Byte Handling18 questions
- Formatting and Parsing18 questions
- Regular Expressions13 questions
- Template Rendering19 questions
- Instants, Durations and Clocks17 questions
- Slices, Maps and Ordering27 questions
- Lazy Sequences17 questions
- Operating System Access102 questions
- Streams and Buffering30 questions
- Files and Directories20 questions
- Paths and Filesystems13 questions
- Program Inputs at Startup13 questions
- Subprocess Control13 questions
- Termination Handling13 questions
- Data Encoding and Storage119 questions
- JSON Marshaling33 questions
- Conversion Interfaces9 questions
- Non-JSON Wire Formats32 questions
- Decoding Untrusted Input14 questions
- Relational Access31 questions
- Network Services and Clients152 questions
- Handlers and Routing22 questions
- Middleware and Request Flow30 questions
- Server Lifecycle and Limits20 questions
- Outbound Requests27 questions
- Streaming and Proxying14 questions
- Sockets and Dialers19 questions
- Request Inputs20 questions
- Security and Cryptography80 questions
- Standard Library Primitives30 questions
- Identity and Credentials18 questions
- TLS Configuration18 questions
- Dependency Risk14 questions
- Service Telemetry68 questions
- Structured Logging With slog13 questions
- Handlers and Log Output23 questions
- Process Self-Reporting21 questions
- Distributed Tracing Hooks11 questions
- Low-Level and Foreign Code79 questions
- Dynamic Types at Runtime29 questions
- Non-Native Build Targets11 questions
- Raw Memory and Layout13 questions
- Calling Into C17 questions
- Kernel-Level Access9 questions
- Idioms and API Design87 questions
- Language Trade-Offs17 questions
- Readability Conventions20 questions
- Value Construction14 questions
- Package Boundaries23 questions
- Anti-Patterns Reviewers Reject13 questions
questions
1,712 · 15 sectionsIn Go, what is the difference between an array `[5]int` and a slice `[]int`?
basics
~20 sAn array's length is part of its type, and assigning or passing one copies every element. A slice is a resizable view onto a backing array, carrying a length and a capacity; copying a slice shares that array.
In Go, what is the difference between a keyed struct literal and a positional one?
basics
~20 sA keyed struct literal names each field and may omit fields, so order does not matter. A positional literal must supply every field in declaration order, so reordering fields silently changes what each value means.
In Go, how do you distinguish a missing map key from one whose stored value is zero?
basics
~20 sUse the two-value comma-ok form: v, ok := m[key]. ok is true only when the key is present. A one-value lookup returns the value type's zero value for a missing key, so 0, an empty string or false cannot tell you which happened.
Go passes every argument by value — how do you write a function that modifies the caller's struct?
basics
~20 sPass a pointer. A function parameter of type Player receives a copy, so writes to it die with the call. Declare the parameter as *Player, call it with &pl, and write p.HP = 40 — Go dereferences the selector for you.
In Go, when does a send block on make(chan int) versus make(chan int, 3)?
basics
~20 sA send on a channel made with make(chan int) blocks until another goroutine is ready to receive. With make(chan int, 3) the send returns immediately while the buffer holds fewer than three values, and blocks only once it is full.
When you send a struct value on a Go channel, does the receiver get a copy or the sender's original?
basics
~20 sA channel send copies the value. The receiver gets its own copy of the struct, so later writes by the sender are invisible. The copy is shallow: pointer, slice and map fields still refer to the same underlying data.
What does close(ch) do to a Go channel, and what do receivers see afterwards?
basics
~20 sclose(ch) marks a Go channel as finished. Receives stop blocking: they deliver any values still buffered, then return the element type's zero value with ok false, and a for range loop over the channel exits.
What does a nil channel in Go do to a goroutine that sends or receives on it?
basics
~10 sBoth operations block forever. A send on a nil channel and a receive from a nil channel park the goroutine permanently, because no counterparty can ever exist. Closing a nil channel panics instead.
What does a `default` case do in a Go `select` statement?
basics
~20 sA default case makes the select non-blocking. If no other case can proceed at that instant, Go runs default immediately instead of waiting. Without a default, the select parks the goroutine until one of its cases is ready.
Why does ranging over the same Go map twice give the entries in a different order?
basics
~20 sGo's runtime starts every map iteration at a randomly chosen position, so map order is undefined and changes from loop to loop. For stable output, copy the keys into a slice, sort it, and index the map in that order.
Why must you write s = append(s, x) in Go instead of just calling append(s, x)?
basics
~20 sappend returns a new slice value and never updates the slice you passed in. If the backing array has no spare room, append allocates a bigger array, so only the returned value is guaranteed to see the appended element.
What three fields does a Go slice header hold at runtime, and what does each mean?
basics
~20 sA Go slice value is three words: a pointer to the first element it covers in a backing array, a length, and a capacity. The elements live in that array, not inside the slice value.
What does a Go string value hold at runtime, and what does assigning one to another variable copy?
basics
~20 sA Go string value is a two-word header: a pointer to an array of bytes plus a length. Assigning or passing a string copies only that header (16 bytes on a 64-bit platform), never the text it points at.
Why does `defer f.Close()` inside a Go for loop keep every file open until the function returns?
basics
~20 sGo's defer is scoped to the function call, not to the block or the loop iteration. Every trip through the loop registers one more deferred Close against the same frame, and none of them run until the enclosing function returns.
What is a sentinel error in Go, and how do you declare one so callers can detect it?
basics
~20 sA sentinel error is one package-level error value, such as var ErrNotFound = errors.New("not found"), that a package returns for a specific condition. Callers detect that condition by matching a returned error against that exported value.
How do you define a custom Go error type that carries structured fields a caller can read?
basics
~20 sDeclare a struct holding the data and give it an Error() string method, normally on a pointer receiver. Any type with that method is usable as an error, and callers read the exported fields off the concrete value.
What does ctx.Err() return after a context is cancelled versus after its deadline passes?
basics
~10 sctx.Err() returns nil while the context is still live, context.Canceled once a cancel function has been called, and context.DeadlineExceeded once the context's deadline has passed. Match them with errors.Is, not with ==.
Why is io.EOF returned by a Read call treated as a normal end of input, not a failure?
basics
~20 sio.EOF is a sentinel error the io.Reader contract uses to say the stream is finished, so nothing went wrong. Callers detect it, stop reading and return success; they never log it or pass it on as a fault.
Given an error from os.Open, how do you check that the file does not exist?
basics
~10 sUse errors.Is(err, fs.ErrNotExist). os.Open returns a *fs.PathError wrapping the operating system's error, so comparing the returned error directly against fs.ErrNotExist is always false; errors.Is unwraps the chain and finds the sentinel.
Go's testing package has no assertEqual - how do you compare a value against an expected one in a test?
basics
~20 sYou compare with ordinary Go code: == for comparable types, reflect.DeepEqual or slices.Equal for composites. On a mismatch call t.Errorf and print both values, conventionally the value you got first, then the value you wanted.
What does declaring `package foo_test` in a _test.go file change versus declaring `package foo`?
basics
~20 sA _test.go file declaring package foo compiles into the package itself and can use its unexported identifiers. One declaring package foo_test compiles as a separate package that must import foo and sees only its exported API.
Why do Go tests keep fixture files in a directory named testdata, and how does the go command treat it?
basics
~20 sThe go command ignores directories named testdata when matching package patterns, so nothing inside is compiled or vetted. A test binary runs with its own package directory as the working directory, so a relative path like testdata/user.go.golden always resolves.
In a Go test, what does t.TempDir() return and who deletes that directory?
basics
~20 st.TempDir() returns the path of a fresh, empty directory created for that test. The testing package removes it automatically once the test and all of its subtests have completed, so the test never writes its own removal code.
What does calling t.Parallel() in a Go test do, and when does that test's body actually run?
basics
~20 st.Parallel() marks a test as safe to run alongside other tests that also call it. The call pauses the test immediately; it resumes only after its parent test function has returned, and then runs concurrently with its parallel siblings.
Why does go/parser.ParseFile need a token.FileSet, and what does it return?
basics
~20 sgo/parser.ParseFile returns an *ast.File, the syntax tree of one source file, plus an error. Node positions are bare integers; the token.FileSet holds each file's name and line table, so those integers decode to file, line and column.
What does a `replace` directive in go.mod do, and how do you point one at a local directory?
basics
~20 sA replace directive in go.mod redirects a module path to something else: either a different module at a stated version, or a directory on disk. The build then compiles that code instead of the version the proxy would serve.
What does `go mod tidy` change in a module's go.mod, and when do you run it?
basics
~20 sgo mod tidy makes the require block match the code: it adds a requirement for every module your packages import, drops requirements nothing imports any more, maintains the indirect markers, and syncs go.sum. Run it after changing imports.
Why must a Go module's path and its import paths end in /v2 once it releases v2.0.0?
basics
~20 sGo treats each major version as a separate module. From v2 on, the module line in go.mod and every import of its packages must end in /v2, so v1 and v2 can live in one build.
What does `go get -u ./...` do in a Go module, and how does `-u=patch` differ?
basics
~20 sgo get -u ./... upgrades every module behind the packages your module imports, directly or transitively, to its newest minor or patch release. go get -u=patch caps each move at the newest patch of the minor already selected.
What does GOTRACEBACK=all change about the output of a crashing Go program?
basics
~20 sBy default a Go crash prints a stack for the goroutine that panicked. GOTRACEBACK=all makes the runtime print a stack for every user-created goroutine instead. It is an environment variable, so it costs a restart, not a rebuild.
When you step through a plain `go build` binary, why do locals read as optimized out, and which build flags fix it?
basics
~20 sGo's compiler keeps locals in registers and inlines small calls, so the variable and the stack frame a debugger looks for do not exist at runtime. Rebuild with go build -gcflags="all=-N -l" to disable optimisation and inlining before stepping.
In Go, how do you capture a panic's stack trace inside the deferred function that recovers it?
basics
~10 sCall runtime/debug.Stack() inside the deferred function. It returns the calling goroutine's formatted stack trace as a byte slice, still containing the frames that panicked. debug.PrintStack() does the same but writes straight to standard error.
What has the Go runtime detected when it prints "fatal error: all goroutines are asleep - deadlock!"?
basics
~20 sEvery user goroutine is parked waiting on another goroutine, with nothing runnable, no armed timer and no thread in a system call. Progress is provably impossible, so the runtime dumps every goroutine stack and exits.
What does a "too many open files" error from net.Dial or os.Open tell you?
basics
~20 sThe process has hit its RLIMIT_NOFILE ceiling on open file descriptors. Every socket, file and open HTTP response body costs one, so the cause is almost always a resource opened on some code path and never closed.
What does Go's cmp.Compare return, and which types can you call it on?
basics
~10 scmp.Compare returns -1 if the first argument is less, 0 if they are equal, and +1 if it is greater. It accepts any cmp.Ordered type: integers, floats, strings, and named types built on them.
In Go's container/heap, which five methods must your type implement?
basics
~20 scontainer/heap.Interface embeds sort.Interface, so your type needs Len, Less and Swap, plus Push(x any) to append one element and Pop() any to remove and return the last one. Five methods, and you own the storage.
Why does ranging over a Go map yield a different order each run, and how do you produce stable output?
basics
~10 sGo deliberately randomises map iteration, so no order is guaranteed or repeatable. For stable output, copy the keys into a slice, sort that slice, then read the map in that key order.
What does slices.Clone(s) copy that the plain assignment b := s does not?
basics
~10 sslices.Clone allocates a new backing array and copies the elements into it, so the two slices no longer share storage. Plain assignment copies only the slice header, leaving both names pointing at one array.
What does slices.Sort do to a slice, and which element types can it sort?
basics
~20 sslices.Sort reorders a slice in place into ascending order and returns nothing. It is generic over element types that support the less-than operator - integers, floats and strings - so no comparison callback or interface implementation is needed.
In Go, how do you let an environment variable set a flag's default so an explicit flag still wins?
basics
~20 sRead the variable with os.LookupEnv before flag.Parse and pass its value as the default argument when you declare the flag. flag.Parse then overwrites that default only for flags that actually appeared on the command line.
In Go, what is the difference between os.Getenv and os.LookupEnv?
basics
~10 sos.Getenv returns only the value, so an unset variable and one set to the empty string both come back as "". os.LookupEnv returns the value plus a presence boolean, keeping those two cases distinct.
In Go's flag package, why does flag.String return a *string, and when may you read it?
basics
~20 sflag.String registers a flag before the command line has been read, so it can only hand back a pointer to storage it will fill in later. Call flag.Parse() first, then read the value as *ptr.
Why write a config file to a temp file and os.Rename it onto the target instead of overwriting in place?
basics
~20 sOverwriting in place leaves the file half-written, so a concurrent reader can load a truncated config. Writing a complete temp file first and calling os.Rename swaps the name in one step: readers see the whole old file or the whole new one.
What does os.ReadFile give you, and what work does it do that you would otherwise write by hand?
basics
~20 sos.ReadFile opens the named file, reads it to the end, closes it, and returns the whole contents as a []byte together with an error. Reaching end of file is not an error, so a successful read returns a nil error.
Why does adding a String() method to a type leave json.Marshal's output unchanged?
basics
~10 sfmt.Stringer is consulted only by the fmt package. encoding/json never looks for String; it looks for json.Marshaler and then encoding.TextMarshaler, and a type with neither is encoded from its structure, field by field.
Why does a []byte struct field marshal to a base64 string in encoding/json?
basics
~20 sJSON has no type for raw bytes, so encoding/json marshals a []byte field as a base64 string using the standard padded alphabet. A nil []byte becomes null and an empty one becomes an empty string.
What do binary.BigEndian.Uint32 and binary.LittleEndian.Uint32 return for the bytes 00 00 01 00?
basics
~10 sbinary.BigEndian.Uint32 reads the most significant byte first and returns 256. binary.LittleEndian.Uint32 reads the least significant byte first and returns 65536. Same four bytes, opposite orders, different numbers.
Why must a gzip.Writer be closed before the file is valid, and what breaks if its Close error is dropped?
basics
~20 sA gzip.Writer buffers compressed data and writes the gzip trailer, a CRC32 plus the uncompressed length, only on Close. Skip Close and the file is truncated; ignore Close's error and a failed final write vanishes silently.
In Go's encoding/csv, when would you use Reader.Read in a loop instead of Reader.ReadAll?
basics
~20 sReadAll builds the whole file as one [][]string in memory and returns nil records if any row fails to parse. Read hands back one record per call and ends with io.EOF, so large or partly malformed files stay workable.
Why must you call resp.Body.Close() on an http.Response, and where does that defer belong?
basics
~20 sAn http.Response body is an open stream on a live connection; closing it releases that connection and its socket. Put defer resp.Body.Close() straight after the error check, because a failed request returns a nil response.
What does Go's http.Client.Timeout field cover, and does it include reading the response body?
basics
~20 shttp.Client.Timeout bounds one whole call: DNS, dial, TLS handshake, sending the request, any redirects, and reading the response body. The clock keeps running after Do returns, so a slow body read fails too. Zero means no timeout.
Why does http.Client.Do return a nil error when the server replies 500?
basics
~10 shttp.Client.Do reports only transport-level failures - DNS, dial, TLS, a deadline, a broken connection. A 500 is a completed HTTP exchange, so err is nil; you must check resp.StatusCode yourself and close the body.
Does Go's http.Get follow a 302 redirect automatically, and how do you find the final URL?
basics
~10 sGo's http.Get follows redirects automatically: http.DefaultClient stops only after ten consecutive hops, so you receive the final response, not the 302. The field resp.Request.URL holds the URL of the last request actually sent.
Why does retrying the same *http.Request send an empty body on the second attempt?
basics
~20 sA request body is a one-shot io.ReadCloser. The first Do drains it and the Transport closes it, so a second Do on the same *http.Request finds nothing left to read. Build a fresh request for every attempt.
How do you build an AES-GCM cipher.AEAD in Go and encrypt one message with it?
basics
~10 sPass the key to aes.NewCipher to get a cipher.Block, wrap that block with cipher.NewGCM to get a cipher.AEAD, then call Seal(dst, nonce, plaintext, additionalData). Seal returns the ciphertext with a 16-byte authentication tag appended.
What does Go's hmac.Equal do that bytes.Equal does not, and what does it return?
basics
~20 shmac.Equal(mac1, mac2 []byte) returns a bool and scans every byte, so its running time does not depend on the contents. bytes.Equal stops at the first differing byte, so its timing reveals how much of the input matched.
What is the difference between sha256.Sum256 and sha256.New, and when do you use each?
basics
~20 ssha256.Sum256 hashes a byte slice you already hold and returns a [32]byte array. sha256.New returns a streaming hash.Hash you write data into and finish with Sum(nil) - use that when the input is a file or stream too large to buffer.
How do you use `hkdf.Key` in Go to turn one master secret into a separate key per purpose?
basics
~10 sCall hkdf.Key once per purpose with a different info string each time, for example "cookie-encryption-v1" versus "url-signing-v1". Same secret, same salt, different info, and you get independent keys you never have to store.
Why is math/rand/v2 unsafe for generating a password-reset token in Go, and what do you use instead?
basics
~20 smath/rand/v2 is a statistical generator that makes no unpredictability promise, so its output can be reconstructed no matter how it is seeded. Use crypto/rand instead: rand.Text() for a token string, or crypto/rand.Read to fill a byte slice.
In Go's log/slog, why does a `slog.Debug` call still cost work when the logger's level is Info?
basics
~20 sGo evaluates every argument before the call runs, so work you do to build a log line happens even at a disabled level. slog only checks the level inside the call, after the arguments are boxed into interfaces.
Which four methods does slog.Handler require, and what is each one for?
basics
~20 sslog.Handler has four methods: Enabled reports whether a level should be logged, Handle formats and writes one Record, WithAttrs returns a new handler carrying extra attributes, and WithGroup returns one that nests later keys under a name.
What does Go's log package write by default: which stream, what prefix, and what severity?
basics
~20 sGo's log package writes to standard error through the one logger returned by log.Default(), prefixing each line with the date and time (log.LstdFlags). It has no severity levels: log.Print, log.Printf and log.Println all produce identical, unlabelled lines.
In Go's log/slog, what does implementing the LogValuer interface on a type change about how it is logged?
basics
~20 sA type that implements LogValuer supplies a LogValue method returning a slog.Value, and slog logs that substitute instead of the value itself. Every handler sees the substitute, so a secret can be replaced with a fixed placeholder in one place.
Should a containerised Go service's slog handler write to os.Stdout or os.Stderr?
basics
~20 sBoth streams are collected by the container runtime, so the real rule is to pick one and send every record there. Most teams choose os.Stdout for the application's log stream and leave os.Stderr to the runtime's own panic output.
In cgo, what do C.CBytes and C.GoBytes do, and who frees the C copy?
basics
~10 sC.CBytes copies a Go []byte into malloc'd C memory and returns an unsafe.Pointer you must release with C.free. C.GoBytes copies C memory back into a brand-new Go slice that the garbage collector owns.
What is syscall.Errno, and how do you test whether an error is a specific errno such as ENOENT?
basics
~20 ssyscall.Errno is an integer type (a uintptr) whose Error method makes the number itself satisfy Go's error interface. Test for a particular errno with errors.Is(err, syscall.ENOENT), which unwraps wrappers such as *os.PathError instead of matching message text.
What do reflect.TypeOf and reflect.ValueOf return, and how do you get an ordinary Go value back?
basics
~20 sreflect.TypeOf returns a reflect.Type describing the dynamic type of whatever you pass it; reflect.ValueOf returns a reflect.Value wrapping the data itself. Value.Interface() hands the data back as an any, which you then type-assert to a concrete type.
What does reflect.New(t) return in Go, and why do you call Elem() on the result?
basics
~20 sreflect.New(t) allocates a zero value of type t and returns a reflect.Value holding a pointer to it, exactly like new(T). Elem() follows that pointer to the value itself, which lives in addressable memory and can be filled in.
Why does reflect.DeepEqual report a nil []string and an empty []string{} as unequal?
basics
~20 sreflect.DeepEqual requires two slices to be both nil or both non-nil before it compares length and elements. A nil slice and an empty slice fail that first check, so it reports false even though both have length zero.
In Go, why do reviewers reject an exported interface declared beside its only implementation?
basics
~20 sGo checks interface satisfaction implicitly, so whoever needs the abstraction can declare it later. An interface written beside its only implementation adds indirection now, hides the concrete type's other methods and fields, and buys nothing.
Why does Go compile a call whose returned error you never check?
basics
~20 sGo's compiler rejects unused variables and unused imports, but not unused return values. A call like f.Close() is a complete statement on its own, so nothing forces you to look at the error it returns.
What is wrong with a package-level `var apiURL = os.Getenv("API_URL")` in a Go package?
basics
~20 sIt runs at package initialization, before main, so nothing can supply or check the value: the package cannot report a missing setting as an error, and a test can only change it by writing to a global.
How should a Go constructor New(cfg Config) supply defaults for fields the caller left at zero?
basics
~20 sNew takes the Config by value, replaces each field still at its zero value with a named package default, and builds the object from that copy. The caller's struct is untouched, and every caller gets identical defaults.
Why do Go libraries write `New(addr string, opts ...Option)` instead of one parameter per setting?
basics
~20 sBecause callers pass only the settings they care about. Option is a function type such as func(*Client); each With... helper returns one, and New starts from a struct holding the package defaults and applies the options to it.