I really value that when working with Go there are no "hidden magic" in source code. Go source code are essentially WYSIWYG. You don't see decorators or dependency injections that might change the behaviors after it is compiled or run that requires you to not only have to understand the language and syntax but also having to learn additional tools' behavior on the source code. While this is true of the language it is not true of the go command for Go's module system.
I've personally found Go modules to be more confusing then the original GOPATH. I understand that it solves some of the complaints about GOPATH and also addresses the diamond dependency problem, but it also adds complexity to the developer workflow and under-the-hood magic. Maybe that's to be expected when it is going beyond source code management and adding a whole package management layer on top, but I'd be much happier to have to deal with this added complexity and burden if the solution was complete (how about package clean up so my mod directory isn't growing non-stop?)!
Modules adds the go.mod file that tracks all a project's dependencies and their versions. This introduces a problem when one is developing both applications and libraries since it is possible that the developer have both the released production version and in-development version of libraries locally. To point your application at the library without constantly changing the import path in source code, the replace directive can be used, but when committing the code it is not ideal to submit the go.mod with the replace directives in it as it will likely break the build for someone else checking out the code and can expose some privacy data (the local path that might contain the user name).
Now developers have to add the replace directives locally, remove them right before submission and then put them back (without typos!). Fortunately, in Go 1.14, the go commands (build, clean, get, install, list, run, and test) got a new flag '-modfile' which allows developer to tell it to use an alternative go.mod file. The allows a production version of go.mod file to not have to be modified during development/debug and a local dev version of go.mod that can be excluded from getting committed (i.e. .gitignored).
This can be done on a per-project level by adding -modfile=go.local.mod to go [build | clean | get | install | list | run | test]:
go build -modfile=go.local.mod main.go
Note that whatever the file name is, it still has to end on .mod since the tool assumes to create a local go.sum mod based on a similar name as the local mod file except with the extension renamed from .mod to .sum.
To apply the use of go.local.mod globally, update "go env":
go env -w GOFLAGS=-modfile=go.local.mod
go env -w will write the -modfile value to where Go looks for its settings:
Defaults changed using 'go env -w' are recorded in a Go environment configuration file stored in the per-user configuration directory, as reported by os.UserConfigDir.
So the flow that Jay Conrad pointed out in this bug thread would be as follows:
- Copy go.mod to go.local.mod.
- Add go.local.mod to .gitignore.
- Run go env -w GOFLAGS=-modfile=go.local.mod. This tells the go command to use that file by default.
- Any any replace and exclude directives or other local edits.
- Before submitting and in CI, make sure to test without the local file: go env -u GOFLAGS or just -modfile=.
- Probably also go mod tidy.