1. Go Fundamentals

A fast orientation to Go's philosophy, modules, toolchain, visibility, and binaries.

Question: What are the key characteristics of the Go programming language?

Answer: Go is a statically typed, compiled language known for its built-in concurrency features, garbage collection, and simple syntax. It compiles to a single binary that is often self-contained; with CGO_ENABLED=0, builds are fully static.

Explanation: Go's design philosophy emphasizes simplicity, readability, and efficiency for modern networked services. Concurrency is a first-class citizen, handled through lightweight goroutines and channels. Error handling is managed explicitly through return values rather than exceptions, and its built-in tooling is powerful and easy to use.

Question: How does Go handle visibility for packages (e.g., public vs. private)?

Answer: Go uses a simple convention based on naming: any identifier (like a variable, function, or type) that starts with a capital letter is exported (public). Identifiers starting with a lowercase letter are unexported (private) to the package.

Explanation: This rule, known as "casing for visibility," eliminates the need for explicit keywords like public or private. It's a core part of Go's idiomatic style and makes code easy to read.

package mypackage

var ExportedVar int // Accessible from other packages
var privateVar int  // Not accessible

Question: What are some of the most common commands in the Go toolchain?

Answer: The most common commands are go run to compile and execute a program, go build to compile a binary, go test to run tests, go fmt to format code, and go mod tidy to manage dependencies.

Explanation: The Go toolchain is known for its simplicity and power. Other critical commands include:

  • go test -race: To detect data races in concurrent code.
  • go vet: A static analysis tool that finds suspicious constructs.
  • go mod init: To initialize a new module.

Question: What are go.mod and go.sum, and how do you manage modules?

Answer: go.mod declares your module path and dependency requirements. go.sum records checksums to verify module contents. Use go mod init to create a module and go mod tidy to add missing and remove unused dependencies.

Explanation: Modules make builds reproducible and secure. Keep both files under version control. The Go proxy ecosystem caches modules; checksums in go.sum ensure integrity. Typical workflow: initialize with go mod init example.com/your/app, import packages in code, then run go mod tidy.

Question: Modules vs GOPATH — what changed and how do I structure projects today?

Answer: Modules replaced GOPATH as the default dependency and build system. Put a go.mod at the repo root; code can live anywhere on disk, not under $GOPATH/src.

Explanation: Since Go 1.16+, modules are the default. Use semantic import paths (module/path/pkg). Pin deps in go.mod, verify via go.sum. For multi-module repos, prefer one module per deployable unless you have a strong reason to split.

Question: What does package main mean, and how do main() and init() work?

Answer: package main builds an executable. The entrypoint is func main(). init() runs before main() and before each package is used; avoid heavy logic there.

Explanation: Each package can have multiple init() functions (file order is unspecified). Init runs after variable initialization and import dependency order. Prefer explicit setup in main() with clear errors rather than hidden work in init().

Question: What is a "zero value" in Go?

Answer: In Go, every variable declared is automatically initialized to its "zero value" if no explicit value is provided. For numeric types, it's 0; for strings, ""; for booleans, false; and for pointers, functions, interfaces, slices, channels, and maps, it's nil.

Explanation: This design choice ensures that variables are always in a predictable state, preventing "uninitialized variable" errors common in other languages. Many Go types, like sync.Mutex or bytes.Buffer, are designed to be useful in their zero-value state.

Question: How do you format and lint Go code idiomatically?

Answer: Use gofmt/go fmt (and goimports) to format automatically; use go vet for static checks; optionally add staticcheck for deeper diagnostics.

Explanation: Formatting is non-negotiable in Go—gofmt enforces a single style. goimports also fixes imports. go vet catches suspicious constructs; staticcheck provides additional best-practice checks.

Question: What role does GOPATH play today with modules?

Answer: You no longer need to keep source under GOPATH. It now primarily hosts the module and build caches (e.g., GOPATH/pkg/mod).

Explanation: With modules, projects can live anywhere on disk. Inspect with go env GOPATH.

Question: What is the difference between a package name and an import path?

Answer: The import path is how other code references your package (e.g., github.com/me/app/pkg/db); the package name is the identifier you write after package and use in code.

Explanation: Package names are usually the last segment of the import path, but need not match directories exactly. Prefer short, meaningful names like db, httpx.