How do internal packages work? Give an example of an allowed and a forbidden import.
Question 416MediumGo 1.22 to 1.25
The compiler enforces this rule: a package whose path contains an internal element can be imported only by code rooted at the parent of that internal directory. It is the main tool for keeping a module's public API small.
example.com/app/
├── internal/db/ // importable by anything under example.com/app/...
├── api/
│ ├── internal/auth/ // importable only under example.com/app/api/...
│ └── handler.go // OK: imports app/api/internal/auth
└── cmd/server/main.go // OK: imports app/internal/db
// ERROR: imports app/api/internal/auth
// another module: import "example.com/app/internal/db"
// => use of internal package not allowed
What the interviewer is looking for: you understand that everything outside internal/ in a published module is public API covered by semver, so helpers should be placed in internal/ by default and promoted later. Mention that the rule works on import paths, so it applies across modules too. Note also that vendor/ and testdata/ are special directories (testdata is ignored by the go tool). A top-level pkg/ directory is only a convention and has no enforced meaning.
More on Modules, Packages & Tooling
- Q414What problem do Go workspaces (go.work) solve? How do they differ from replace?
- Q415How does vendoring work in module mode, and when is it still worth using?
- Q417Your CI can't fetch a private module from github.com/acme/secret. Walk through GOPROXY, GOPRIVATE, GONOPROXY, GONOSUMDB and GOINSECURE.
- Q418How does the Go checksum database protect the supply chain? What does go mod verify actually check?
- Q419What do the go and toolchain directives mean since Go 1.21, and how does GOTOOLCHAIN behave?
- Q420What does this print? (Hint: consider the go line in go.mod.)