For a Go module, what does `go list -m all` print that `go mod graph` does not?
answer
- edges versus the resolved result
- one line per requirement, not per module
- the same module can appear twice in one output
- only one version of a module compiles
- one is an inventory, the other is inputs
basics
~20 sgo list -m all prints the build list: the one version of each module that the build actually uses. go mod graph prints raw requirement edges, one per line, and the same module can appear there at several versions, only one of which is selected.
solid answer
~50 sThe **build list** is the resolved answer to "what does this build consist of" — exactly one version per module path, computed from the main module's requirements plus the requirements recorded in each dependency's own go.mod. `go list -m all` prints it, one module and version per line, and `go list -m -json all` gives the same thing machine-readably. `go mod graph` prints the *input* instead: one line per requirement edge, `requirer@version required@version`, with the main module appearing without a version on the left. The graph is bigger and contains versions that lost — if two dependencies require the same module at different minimums, both edges show up, but only one version reaches the build list. So the graph is what you read to answer "who asks for this, and at what version?", and the build list is what you read for an inventory of what actually compiles.
code
text · 8 lines$ go mod graph
example.com/billing example.com/[email protected]
example.com/billing example.com/[email protected]
example.com/[email protected] example.com/[email protected]
example.com/[email protected] example.com/[email protected]
$ go list -m example.com/text
example.com/text v0.9.0go deeper
Know the two commands exist and what each prints: one gives the modules and versions your build uses, the other gives raw requirement lines. Being able to name go list -m all is enough here.
Explain the difference in kind: edges versus a resolved set, why a module can appear at two versions in the graph, and why the main module has no version in either output.
Show the investigation order — build list, then why, then graph — and know which output is defensible as an inventory of what actually ships in the binary.
Decide what the organisation treats as the source of truth for dependency reporting, and make sure audits and scanners read the resolved build list rather than a graph dump that overstates it.
## Two different questions There are two module-level questions that look similar and have different answers: 1. *What did everybody ask for?* — the **module requirement graph**. 2. *What did the go command settle on?* — the **build list**. `go mod graph` answers the first. `go list -m all` answers the second. ## The build list The build list is the set of module versions used to build the packages of the main module: **exactly one version per module path**. It is derived from the main module's `require` lines plus the `require` lines in the go.mod of each module in the graph. Which version wins when several are requested is the job of minimal version selection; the result of that process is the build list. ``` $ go list -m all example.com/billing example.com/httpx v1.4.0 example.com/logging v1.2.0 example.com/text v0.9.0 ``` The main module is printed first, without a version, because it is the thing being built rather than a dependency of it. `go list -m <path>` prints the selected version of one module; `go list -m -json all` emits structured records with fields such as the version, whether the module is indirect, and where it sits in the module cache. This is the output to feed anything that needs an inventory — a licence report, a dependency review, an answer to "are we building that module at all?". It is the truth about the binary. ## The requirement graph `go mod graph` prints one line per edge: ``` example.com/billing example.com/[email protected] example.com/billing example.com/[email protected] example.com/[email protected] example.com/[email protected] example.com/[email protected] example.com/[email protected] ``` Each line reads "the module on the left requires the module on the right at that version". The main module appears on the left without a version. Note `example.com/text` on two lines at two versions: both are genuine requirements, but the build list contains only one of them. Nothing here is wrong or stale — a graph edge is a *request*, not a result. That is why the graph is almost always larger than the build list, sometimes by an order of magnitude, and why counting lines from `go mod graph` is a bad way to estimate how many dependencies you have. ## Module graph versus package graph A further distinction worth stating out loud in an interview: both of these commands work at **module** granularity. A module can contain hundreds of packages and your build may compile three of them. The import graph of packages is a different object — `go list -deps ./...` walks that one. "We depend on module M" and "we compile every package in M" are not the same claim. ## Pruning makes `all` narrower than it once was On modern `go` lines the module graph is pruned: the go command does not need to load the go.mod of every module beneath every dependency, because your own go.mod records the modules supplying transitively imported packages. One visible consequence is that `all` means "the modules providing packages transitively imported by packages and tests in the main module", not "every module anyone anywhere mentioned". That is what you want for an inventory, and it is another reason the two outputs diverge in size. ## Using them together The practical workflow when a module appears in your build and you do not know why: - `go list -m <module>` — is it in the build list at all, and at what version? - `go mod why -m <module>` — which chain of packages reaches it? - `go mod graph | grep <module>` — which modules requested it, and at which versions? The first tells you the outcome, the second tells you the purpose, the third tells you the inputs. Reaching for the graph first is the common mistake: it is the largest and noisiest of the three, and it answers a question you usually only need after the other two.
- Where does the build list come from when go.mod lists only part of it?The go command reads the go.mod of each required module for its own requirements and combines them with yours, then selects one version per module path. The build list is that combined result, which is why it can contain modules your own file never names.
- Which of the two outputs would you feed to a dependency inventory, and why?`go list -m all`, ideally with `-json`. It is exactly the set of module versions the build uses, one per path. `go mod graph` over-reports: it includes edges to versions that were never selected, so an inventory built from it lists modules and versions that are not in your binary.
- How do you check what a single module resolved to?`go list -m example.com/x` prints that module and its selected version, or reports that it is not in the build list. It is the quickest confirmation that a version you expected actually won, without reading the whole graph.
saying these in an interview costs you the question
- Reads go mod graph as the list of what gets compiled
- Assumes every version printed by go mod graph is downloaded
- Thinks go.mod alone contains the whole build list
- Confuses the module graph with the package import graph
- Counts graph lines to estimate dependency count