Go version

go 1.24 linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/dkoptev/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/dkoptev/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build581616536=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/dkoptev/cyp-services/cyp-firmware-manager/go.mod'
GOMODCACHE='/home/dkoptev/go/pkg/mod'
GONOPROXY='yadro.dev/*'
GONOSUMDB='yadro.dev/*'
GOOS='linux'
GOPATH='/home/dkoptev/go'
GOPRIVATE='yadro.dev/*'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go-1.24.5'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/dkoptev/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go-1.24.5/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.5'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

When I import some third-party package of some third-party module, Go has an optimization that adds only dependencies of the package being imported to the my go.mod, but not all dependencies of the third-party module. This is good optimization because only dependencies of the imported package need to be statically linked into my binary during build.

But I have a problem when dependencies are common to the my module and to the third-party module, but not needed in the package I'm importing from this third-party module. Optimization does not work here.

To be more specific:

  1. My module has a dependency on, let's say, go.uber.org/zap/exp
  2. I want to import the package hello of the third-party module example.com/abc/example-module, so I do

    go import "example.com/abc/example-module/hello"

  3. The module example-module also has go.uber.org/zap/exp in its go.mod, but its hello package does not import it. If I didn't have the dependency on zap/exp, everything would be fine and I wouldn't even see zap/exp in my go.mod. But my code also depends on zap/exp.

  4. When I do go get example.com/abc/example-module@some_version, I see that go.uber.org/zap/exp is also updated in my go.mod since it is newer in example.com/abc/example-module go.mod.

What did you see happen?

zap/exp is also updated when I'm updating the module that depends on it

go: upgraded go.uber.org/zap/exp v0.1.0 => v0.2.0

What did you expect to see?

Since hello package does not import go.uber.org/zap/exp, I'd expect that there is no need to bump zap/exp similar to how there is no need to add a new dependency to my go.mod if it is only present in the third-party module's go.mod, but not imported in its hello package. It should be ok to keep linking with the old zap version, that my code needs. But the optimization does not work here.

Could you tell me, please, is this behavior expected? Can we fix it and apply the same optimization?

Comment From: mknyszek

CC @golang/command-line

Comment From: seankhliao

I believe this is working as intended. An entry in go.mod records resolved versions, which is necessary when the dependency graph is more complex. It doesn't imply that a dependency is linked into the final binary.

There is nothing to fix.

Comment From: gabyhelp

Related Issues

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

Comment From: denis-koptev

@seankhliao thanks for your answer! I have one more question. If entries in go.mod are about dependent modules' versions why do we have an optimization that skips dependencies which are present in a third-party go module but are not imported in a package within this module that we import in our code? And why don't we have the same optimization if I import the same package of the same module, but unfortunately I have the same dependent module as third-party module has, but third-party package I'm importing in my code does not import this dependency? It seems pretty the same...