Hi all,
Modules have come a long way in the past few years, and I think a lot of
the initial confusion and common questions around them are now fairly
well understood in general.
From what I've been seeing on the issue tracker and forums, as well as
from colleagues at work, it seems like we have one major common
misconception left: what the go.mod and go.sum files actually mean.
go.sum is the easier one. Many users fall into the initial assumption
that go.sum is a form of lock file, as it seems to list versions for all
direct and indirect dependencies. And they might even use modules for
years without realising that that's not the case.
And the misunderstanding is reasonable, especially given that many other
package managers also use two files: one to declare the package and list
its dependencies, and another to lock their versions. And since go.mod
declares the module, it's only natural to expect go.sum to be a form of
lock file.
go.mod is a trickier one. I think many users think they can directly
consume and edit the go.mod file, when in practice trying to do that has
many nuances. Here are some examples of confusion I've seen recently:
* I want to upgrade a dependency version.
When I add a line to go.mod, "go mod tidy" removes it.
* I only want to upgrade dependency A in my go.mod.
"go mod tidy" is then upgrading or removing other dependencies too.
* I want to downgrade a dependency version in my go.mod.
"go mod tidy" undoes the downgrade.
* I want to see my deps and their versions, including indirect deps.
Reading go.mod mostly works, but some deps are missing.
The root causes of these unexpected behaviors can vary depending on the
situation. But at the core, the issue is always the same: the user is
attempting to directly understand or edit go.mod.
And that's tricky: to do so, you at least need to fully understand SIV.
Not only that, the rules that "go mod tidy" follows for pruning go.mod
aren't easy to grasp, and they'll get trickier for 1.17.
It's worth noting that the aging Modules wiki article, now largely
superseded by
https://golang.org/ref/mod, tackles some of these topics:
https://github.com/golang/go/wiki/Modules#faqs--gomod-and-gosum
And I think that, if a user sat down and spent two hours digesting
/ref/mod, they would probably get a better grasp.
That said, I think it's naive to assume that users will carefully read
that doc, let alone stay up to date with it and perfectly understand how
the go.mod and go.sum files work at a given moment.
I think our best bet is to actively encourage people to use modules
through cmd/go. For example, using "go list -m" to see the list of
currently used modules and their versions, and "go get" to update
dependencies.
One example of a greatly under-utilised command is "go list -m all".
Far too many users are filling this need by reading go.mod directly.
(Worth noting that looking at go.mod diffs in PRs/commits can still be
useful to get a sense of what's changed, even without understanding MVS
and go.mod tidying. I don't think we need to fight against that.)
As much as I like canonical documentation pages like /ref/mod, I think
this kind of usability outreach best matches a post on
blog.golang.org.
Think like
https://blog.golang.org/go1.13-errors, which brought many
gophers up to date with new ways to use errors, but for go.mod and
go.sum files. That blog post is still useful as an introduction today.
I think it would be fine for the content to be written once and mostly
left static. The advice to generally not try to consume or edit go.mod
and go.sum directly should be timeless for the foreseeable future.
Perhaps the way we use "go get" or "go mod" will change over time, and
for that reason the blog post should remain brief and link to /ref/mod
for details and examples.
I'm sure there are more plans for content on modules. This is just my
current impression and a hopefully useful take on a solution :)