I reproduced a build failure only after deleting my GOPATH. That sentence contains most of the problem.

My normal workspace had this shape:

$GOPATH/
    src/example.com/service
    src/github.com/some/dependency
    pkg/darwin_amd64
    bin

go get fetched the dependency’s current source into the expected import path. My machine had an older checkout that still provided a function the service used. A fresh machine fetched a newer revision where the function had changed. The import path named a repository location, not the exact source revision I had tested.

I briefly considered copying dependencies under a random directory in the service. In 2014 the go command does not define vendoring semantics that make such a copy transparently override the canonical import. Rewriting imports to local copies creates new package identities and a permanent maintenance chore.

For deployable applications, I record the repository revisions and make the build checkout those revisions into an isolated GOPATH. Some teams use scripts; others use emerging third-party dependency tools. None is yet the universal answer. Libraries have an even harder choice because pinning their transitive world can conflict with the application assembling the final program.

I also keep import paths canonical. Relative imports make a package depend on where it happens to sit and behave poorly outside a narrow local experiment.

The decisive experiment was a new empty workspace:

$ mkdir /tmp/service-build
$ GOPATH=/tmp/service-build go get example.com/service/cmd/server

That made every source checkout visible under one disposable root. I recorded git rev-parse HEAD for each dependency and reproduced the working set by checking out those revisions. The script fails if a revision cannot be fetched; silently falling back to a branch tip would restore the original uncertainty.

Import-path compatibility remains social rather than solver-enforced. If upstream makes an incompatible change at the same path, the go command cannot select “the old API” from the import statement. Forking under a new path changes package identity, so values from original and forked packages are distinct even when declarations match.

For every recorded revision I also keep its canonical repository URL. Import paths can redirect discovery, and repeatability requires knowing both what commit was selected and where the build expects to obtain it.

GOPATH solves workspace layout and import discovery well. It does not solve repeatable dependency selection. Saying that plainly avoids a lot of mystical advice about cleaning $GOPATH/pkg. My build was not stale; its input was unspecified.