GOPATH is a workspace, not a package
I set GOPATH to the directory containing one package and then wondered why go install produced a peculiar path. The variable was innocent. I had mistaken the whole workspace for one shelf in it.
A minimal workspace has a recognizable shape:
$GOPATH/
src/example.org/hello/hello.go
pkg/
bin/
Source lives below src in directories corresponding to import paths. Installed package objects go below pkg, arranged for the target system. Installed commands go in bin. GOPATH names the root containing those areas, not src itself and not the package directory.
The surprise was that import paths and disk paths line up without making imports relative. Code can import example.org/hello; the go command searches the workspace’s src/example.org/hello. The source does not need to know how many .. components separate two packages on my machine.
This is a workspace convention, not a global registry. I can keep more than one workspace and select one through the environment. A GOPATH list can also provide multiple roots, although source and installation behaviour deserve attention when doing that. For a small project, one root is easier to reason about.
I prefer an import path that reflects where a package is expected to live, even before publishing it. It avoids renaming every import when the code leaves a scratch directory. I also keep third-party packages in the workspace layout rather than copying their files into my package and erasing their identity.
The caveat is that the workspace does not pin dependency versions. If I update a dependency in place, every package using that workspace sees the update on its next build. Reproducible releases therefore still require discipline about the exact source revisions checked out. The layout organizes code; it does not make dependency history disappear.
There is also no need to set GOROOT to the workspace. GOROOT identifies the Go installation and standard library. GOPATH identifies my code and other non-standard packages. Giving both variables the same value is less configuration than performance art.
After moving my package under src and pointing GOPATH at the parent workspace, go build, go test, and go install behaved as advertised. The directory tree is opinionated, but at least it has the courtesy to be visible.