How do you structure a large Go project (cmd/, internal/, pkg/ debate)? How should packages be named and split to avoid stutter and cycles?
A common layout: cmd/<binary>/main.go holds thin mains that only do wiring. internal/ holds most of the code; the compiler enforces that only code rooted at internal's parent can import it, so you can refactor freely. pkg/ is only a convention for "importable by others". Many Go team members consider it noise, and golang-standards/project-layout is not an official standard. Start flat. Add structure when you have several binaries or a public API.
myapp/
go.mod
cmd/api/main.go // flag parsing, wiring, run
cmd/worker/main.go
internal/order/ // domain: types + service + interfaces it needs
internal/postgres/ // adapter implementing order.Repository
internal/httpapi/ // transport
Naming: short, lowercase, single-word nouns. Avoid util, common, helpers, models, because they attract unrelated code. Avoid stutter: the caller writes order.Service, not order.OrderService, and http.Server, not http.HTTPServer. Split by domain/responsibility, not by layer: packages named controllers/, services/ and models/ tend to create cycles.
Avoiding import cycles (a compile error in Go): (1) point dependencies inward, so domain packages import nothing from adapters; (2) define interfaces in the consuming package so the implementation depends on the consumer, not the reverse; (3) move shared types into a small leaf package; (4) merge two packages that always change together. Interviewer angle: package boundaries are API boundaries. Minimize exported identifiers and mention internal/ enforcement.
More on Go Idioms, Design Patterns & Language Design
- Q554Can you compare functions in Go? Why is a func only comparable to nil, and how does that affect using funcs as map keys?
- Q555Does Go do tail-call optimization? What happens with deep recursion, and what does "goroutine stack exceeds 1000000000-byte limit" mean?
- Q557How do you do dependency injection in Go? Compare manual constructor wiring with google/wire and uber/fx.
- Q558How would you implement the decorator, strategy, and adapter patterns idiomatically in Go?
- Q559How do you implement a circuit breaker and bulkhead in Go? What state machine and concurrency concerns are involved?
- Q560What are the uses of the blank identifier (_)? Explain import _, var _ = , _ = x and blank struct fields.