Should dev tools be tracked by go.mod?

155 views
Skip to first unread message

Sven Anderson

unread,
Jan 24, 2022, 1:42:49 PM1/24/22
to golang-nuts

Hi everyone,


In our project we are having a dispute on a very similar topic as the one raised by Rodolfo Carvalho recently, and input from this forum might help us to settle it.


The situation:


Our project is a Go binding for a C API, basically one package per C library. In order to track which C API we already have a binding for, we wrote a small tool that scans both the C headers and the Go code, matches them up and outputs a report. The tool is used by developers and by the CI, but not by users of the Go bindings. The tool does not make changes to the source code of the library, it only analyses and reports on it. However, the dependencies of this tool will end up in the "require" section of the general go.mod file of the whole module. 


Our team has yet to come to an agreement on the best approach around go.mod use in this case. The two positions are the following:


1) The tool's dependencies should also be tracked in the general go.mod file.

- Code dependencies are only expressed by "import" statements in the Go code.

   - At the moment the tool is very specific to this project and not usable in other projects.

  - go.mod is about tracking versions, and pinning the versions of the tools dependencies to the code is the right thing.

   - Having a separate go.mod for each internal tool makes things more complicated (as also pointed out in the FAQ):

     * Other tools like vet and gofmt must be explicitly executed for each module, "./..." doesn't work for all the code anymore.

     * Precautions must be taken that the local module of the tool is used, and not another version.

     * In theory the general go.mod file would have to include a pinning for the internal tool then (via a tools.go file).

     * There might be "replace" directives necessary.


2) The tool should have its own separate go.mod file.

   - The tool is not part of the code base and not necessary to build the Go bindings.

   - It feels cleaner to have a clear point of separation between the library and the tool.

   - It avoids unnecessary downloads in some situations.

   - Entries in the main go.mod could falsely advertise a dependency to users.

   - 3rd-party tools like packagers and security analysis tools might incorrectly interpret go.mod entries falsely as dependencies.

   - If the tool will get more generic, it might be easier to remove it from the repository.


We are glad to hear any opinions and other recommendations on how to settle the dispute. :-)


Thanks,


Sven (@ansiwen)


Sean Liao

unread,
Jan 24, 2022, 3:00:56 PM1/24/22
to golang-nuts
As a (hypothetical) consumer of your library, I would definitely appreciate fewer dependencies, 
mostly to reduce the chance of (or difficulty of resolving) conflicts with any other dependencies I may have 
(especially painful when incompatible v0.X.Y transitive dependencies are used).

It seems your tooling is developed in tandem with the library,
this certainly increases the draw of versioning everything together as a single codebase.
But If everything is in a single repo it may be reasonable to not have any explicit connection between the 2 modules,
after all, if the libarary module pins the tool module, then you're back to bringing in all its dependencies.

Most of my experience has been with external tooling, and here I prefer using `go install pkg@version`
over pinning with go.mod as this ensures no interference in dependency versions between tools,
and can be put in the same place as the other scripting that makes use of the tools.

Sven Anderson

unread,
Jan 24, 2022, 4:45:29 PM1/24/22
to Sean Liao, golang-nuts
Hi Sean,

Thanks a lot for your answer!

On Mon, Jan 24, 2022 at 9:01 PM Sean Liao <seank...@gmail.com> wrote:
As a (hypothetical) consumer of your library, I would definitely appreciate fewer dependencies, 
mostly to reduce the chance of (or difficulty of resolving) conflicts with any other dependencies I may have 
(especially painful when incompatible v0.X.Y transitive dependencies are used).

Are conflicts really possible? Let's call the dependency of our internal tool "foo". So, if I'm not mistaken, conflicts could not appear, because as a user of our library the Go tooling would only follow the import graph, and because our library is not using "foo" (only our helper tool is) it would never evaluate our go.mod entry for "foo". If your own code has another dependency that is using "foo", it would only evaluate the go.mod file of _that_ dependency, and there is no conflict possible. Am I getting this wrong?

Kevin Wan

unread,
Jan 25, 2022, 2:04:32 AM1/25/22
to golang-nuts
I'm separating go.mod files for framework and cli tool in our project. It works well.

Paul Jolly

unread,
Jan 25, 2022, 10:32:22 AM1/25/22
to Sean Liao, golang-nuts
I have nothing to add to Sean's excellent reply, other than to mention...

> Most of my experience has been with external tooling, and here I prefer using `go install pkg@version`
> over pinning with go.mod as this ensures no interference in dependency versions between tools,
> and can be put in the same place as the other scripting that makes use of the tools.

There is also `go run pkg@version` which obviates setting PATH at the
expense of slightly more command startup overhead.

Regarding that overhead, on a recent tools call Michael Matloob from
the cmd/go team mentioned they were looking into optimisations that
might reduce the overhead of `go install pkg@version`.

Sean Liao

unread,
Jan 25, 2022, 3:55:17 PM1/25/22
to golang-nuts
Go's dependency resolution doesn't work at that fine-grained of a level,
it works on the module (unit of versioning) graph, not the package import graph.
If your library is imported and used,  all the dependency versions in your `go.mod` participate in version selection.
This could present a problem if your tools introduce a version higher than what your consumer would otherwise have selected.

Reply all
Reply to author
Forward
0 new messages