Generated by depstat at commit
d8d43a90343on branchmaster. Analysis date: 2026-04-24. Compared against:v1.35.4. depstat version:45eb8b409b5280a128d6bcbc974952c599954e1d(Fix why --svg/--dot timeout on highly-connected deps like OTel).
| Metric | Value |
|---|---|
| Total unique dependencies | 255 |
| Direct dependencies | 178 |
| Transitive-only dependencies | 222 |
| Test-only dependencies | 31 |
| Non-test dependencies | 224 |
| Dependency graph max depth | 19 |
| Dependency cycles | 19 |
| Archived dependencies | 5 |
| Main modules (go.work) | 34 (1 root + 33 staging) |
- Master is leaner than v1.35.4. −11 total dependencies net (17 removed, 6 added). This continues the multi-release trend of reducing the dependency surface rather than growing it.
github.com/pkg/errorsfinally retired. The long-standing archived error-wrapping library has been removed from master. All callers now use native Gofmt.Errorf/%wwrapping.github.com/docker/dockersplit intogithub.com/moby/moby/api+github.com/moby/moby/client, establishing cleaner module boundaries aligned with the Moby project's own restructuring.github.com/bufbuild/protovalidate-go→buf.build/go/protovalidate: module path migrated to the buf.build registry, reflecting the Buf ecosystem's move to a canonical registry domain.go-grpc-prometheusremoved, consolidated intogrpc-middleware/providers/prometheus(already a dependency since v1.35.x).- Two new staging modules —
k8s.io/cri-streamingandk8s.io/streaming— extract CRI streaming functionality into dedicated, independently importable packages. - gRPC jumped 8 minor versions (v1.72.2 → v1.80.0), the largest single-library leap in this cycle.
- OTel contrib packages advanced 7–24 minor/patch versions (e.g.,
otelrestfulfrom v0.44 → v0.68). - Go 1.26 — a significant toolchain upgrade.
github.com/go-logr/logris the single highest-fan-in non-toolchain external library at 80 direct dependents — nearly every staging module depends on it for structured logging.gopkg.in/yaml.v3has 91 direct dependents but remains on Kubernetes' unwanted-dependencies list; it persists purely as a transitive dependency through etcd, testify, and OTel modules.
Kubernetes uses a go.work monorepo with vendored dependencies and a staging publication mechanism that makes it unique in the Go ecosystem.
k8s.io/kubernetes/
├── go.work -> workspace covering root + 33 staging modules
├── go.mod -> k8s.io/kubernetes (root module)
├── vendor/ -> vendored copy of all external dependencies
└── staging/src/ -> 33 k8s.io/* and sigs.k8s.io/* staging repos
├── k8s.io/api/
├── k8s.io/apimachinery/
├── k8s.io/apiserver/
├── k8s.io/client-go/
├── k8s.io/cri-streaming/ <-- NEW in this cycle
├── k8s.io/streaming/ <-- NEW in this cycle
└── ... 28 more
Key architectural properties:
- All
k8s.io/*staging modules are part of the workspace and share a single vendor directory - External dependencies are vendored at the root, deduplicated across all 34 modules
depstattraverses the full go.work graph, not just the root module
34 modules detected in go.work:
k8s.io/api, k8s.io/apiextensions-apiserver, k8s.io/apimachinery, k8s.io/apiserver, k8s.io/cli-runtime, k8s.io/client-go, k8s.io/cloud-provider, k8s.io/cluster-bootstrap, k8s.io/code-generator, k8s.io/component-base, k8s.io/component-helpers, k8s.io/controller-manager, k8s.io/cri-api, k8s.io/cri-client, k8s.io/cri-streaming (new), k8s.io/csi-translation-lib, k8s.io/dynamic-resource-allocation, k8s.io/endpointslice, k8s.io/externaljwt, k8s.io/kms, k8s.io/kube-aggregator, k8s.io/kube-controller-manager, k8s.io/kube-proxy, k8s.io/kube-scheduler, k8s.io/kubectl, k8s.io/kubelet, k8s.io/kubernetes, k8s.io/metrics, k8s.io/mount-utils, k8s.io/pod-security-admission, k8s.io/sample-apiserver, k8s.io/sample-cli-plugin, k8s.io/sample-controller, k8s.io/streaming (new)
Direct Dependencies: 178
Transitive-only: 222
Total (deduplicated): 255
Max Depth: 19
Test-only: 31
Non-test: 224
The 31 test-only dependencies are unchanged from v1.35.4, meaning all of the net −11 reduction came from the non-test (production) dependency surface — a clean signal that this cycle's pruning was disciplined and targeted.
Fan-in = number of modules in the workspace that directly depend on a package. Higher fan-in = more structural load-bearing.
| Rank | Fan-in | Module |
|---|---|---|
| 1 | 105 | github.com/davecgh/go-spew |
| 2 | 97 | gopkg.in/yaml.v3 |
| 3 | 96 | github.com/stretchr/testify |
| 4 | 96 | github.com/pmezard/go-difflib |
| 5 | 88 | google.golang.org/protobuf |
| 6 | 80 | github.com/go-logr/logr |
| 7 | 64 | github.com/google/go-cmp |
| 8 | 59 | go.yaml.in/yaml/v2 |
| 9 | 55 | github.com/modern-go/reflect2 |
| 10 | 55 | github.com/modern-go/concurrent |
| 11 | 54 | github.com/json-iterator/go |
| 12 | 53 | github.com/google/uuid |
| 13 | 52 | gopkg.in/inf.v0 |
| 14 | 52 | google.golang.org/genproto/googleapis/rpc |
| 15 | 52 | github.com/x448/float16 |
| 16 | 51 | github.com/spf13/pflag |
| 17 | 51 | github.com/fxamacker/cbor/v2 |
| 18 | 48 | google.golang.org/grpc |
| 19 | 48 | go.opentelemetry.io/otel |
⚠️ = listed inhack/unwanted-dependencies.json; present only as transitive dependencies pulled by other libraries, not directly by Kubernetes code.
The high fan-in of unwanted modules like go-spew (105), yaml.v3 (97), and json-iterator/go (54) reflects how deeply embedded these are in the transitive dependency graph — they appear because popular libraries like testify, etcd, and OTel all require them.
All 19 cycles detected originate in upstream ecosystem packages. Kubernetes's own k8s.io/* staging repos maintain perfectly acyclic internal layering.
| Cycle Length | Count |
|---|---|
| 2 | 7 |
| 3 | 6 |
| 4 | 4 |
| 5 | 2 |
| # | Cycle |
|---|---|
| 1 | github.com/golang/protobuf → google.golang.org/protobuf → github.com/golang/protobuf |
| 2 | github.com/onsi/ginkgo/v2 → github.com/onsi/gomega → github.com/onsi/ginkgo/v2 |
| 3 | github.com/prometheus/client_golang → github.com/prometheus/common → github.com/prometheus/client_golang |
| 4 | github.com/stretchr/objx → github.com/stretchr/testify → github.com/stretchr/objx |
| 5 | go.opentelemetry.io/auto/sdk → go.opentelemetry.io/otel → go.opentelemetry.io/auto/sdk |
| 6 | go.opentelemetry.io/auto/sdk → go.opentelemetry.io/otel → go.opentelemetry.io/otel/metric → go.opentelemetry.io/auto/sdk |
| 7 | go.opentelemetry.io/auto/sdk → go.opentelemetry.io/otel/trace → go.opentelemetry.io/otel → go.opentelemetry.io/auto/sdk |
| 8 | go.opentelemetry.io/auto/sdk → go.opentelemetry.io/otel/trace → go.opentelemetry.io/otel → go.opentelemetry.io/otel/metric → go.opentelemetry.io/auto/sdk |
| 9 | go.opentelemetry.io/otel → go.opentelemetry.io/otel/metric → go.opentelemetry.io/otel |
| 10 | go.opentelemetry.io/otel → go.opentelemetry.io/otel/metric → go.opentelemetry.io/otel/trace → go.opentelemetry.io/otel |
| 11 | go.opentelemetry.io/otel → go.opentelemetry.io/otel/trace → go.opentelemetry.io/otel |
| 12 | go.opentelemetry.io/otel/sdk → go.opentelemetry.io/otel/sdk/metric → go.opentelemetry.io/otel/sdk |
| 13 | golang.org/x/crypto → golang.org/x/net → golang.org/x/crypto |
| 14 | golang.org/x/crypto → golang.org/x/text → golang.org/x/tools → golang.org/x/net → golang.org/x/crypto |
| 15 | golang.org/x/crypto → golang.org/x/text → golang.org/x/mod → golang.org/x/tools → golang.org/x/net → golang.org/x/crypto |
| 16 | golang.org/x/mod → golang.org/x/tools → golang.org/x/mod |
| 17 | golang.org/x/mod → golang.org/x/tools → golang.org/x/net → golang.org/x/text → golang.org/x/mod |
| 18 | golang.org/x/net → golang.org/x/text → golang.org/x/tools → golang.org/x/net |
| 19 | google.golang.org/genproto/googleapis/api → google.golang.org/grpc → google.golang.org/genproto/googleapis/api |
| Module | Cycle Count |
|---|---|
go.opentelemetry.io/otel |
7 |
go.opentelemetry.io/auto/sdk |
4 |
go.opentelemetry.io/otel/metric |
4 |
go.opentelemetry.io/otel/trace |
4 |
golang.org/x/net |
5 |
golang.org/x/tools |
5 |
golang.org/x/text |
4 |
golang.org/x/crypto |
4 |
OpenTelemetry accounts for 9 of the 19 cycles — nearly half. This is a known upstream design: the OTel Go SDK splits its API surface across otel, otel/metric, otel/trace, otel/sdk, otel/sdk/metric, and otel/auto/sdk modules that mutually depend on each other for interface definitions and default no-op implementations.
The diagram below shows the 9 OTel cycles with exact version numbers on each edge. Dashed red arrows show where auto/sdk requires an older version (v1.38.0) that MVS resolves upward to v1.43.0:
The golang.org/x packages account for 6 cycles through a well-known pattern: x/crypto, x/net, x/text, x/tools, and x/mod form a web of mutual test imports and toolchain dependencies.
| Module | Version | Risk | Notes |
|---|---|---|---|
github.com/flynn/go-shlex |
v0.0.0-20150515145356-3f9db97f8568 | Low | Simple shell lexer; stable, minimal attack surface |
github.com/google/btree |
v1.1.3 | Low | Functionally complete B-tree; no security surface |
github.com/google/gofuzz |
v1.0.0 | Low | Active replacement (sigs.k8s.io/randfill) in progress |
github.com/json-iterator/go |
v1.1.12 | Medium | Production JSON serialization in API server hot paths |
github.com/kr/pty |
v1.1.1 | Low | Superseded by github.com/creack/pty; minimal API |
github.com/pkg/errors — the archived error-wrapping library — has been removed in this cycle. All call sites now use native Go error wrapping (fmt.Errorf with %w, errors.Is, errors.As). This reduces the archived count from 6 (v1.35.4) to 5 on master.
depstat archived can only check GitHub-hosted repos. The following are hosted outside GitHub and could not be verified programmatically: bitbucket.org/bertimus9/systemstat, buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go, cyphar.com/go-pathrs, gonum.org/v1/gonum, google.golang.org/protobuf, gopkg.in/* variants. All are actively maintained.
┌────────────────────────┬──────────┬─────────────────┬─────────┐
│ Metric │ v1.35.4 │ master HEAD │ Delta │
├────────────────────────┼──────────┼─────────────────┼─────────┤
│ Direct Dependencies │ 186 │ 178 │ −8 │
│ Transitive Dependencies│ 228 │ 222 │ −6 │
│ Total (deduplicated) │ 266 │ 255 │ −11 │
│ Max Depth │ 19 │ 19 │ 0 │
└────────────────────────┴──────────┴─────────────────┴─────────┘
| Module | Notes |
|---|---|
buf.build/go/protovalidate |
Replaces github.com/bufbuild/protovalidate-go (module path migration) |
github.com/cenkalti/backoff/v5 |
Upgraded from v4 (vendor-only); v5 is a direct dependency |
github.com/moby/moby/api |
Split from github.com/docker/docker |
github.com/moby/moby/client |
Split from github.com/docker/docker |
go.opentelemetry.io/otel/exporters/stdout/stdouttrace |
New OTel stdout trace exporter |
gonum.org/v1/gonum |
Numerical/scientific computing library (new) |
| Module | Notes |
|---|---|
github.com/armon/circbuf |
Unmaintained; call site removed |
github.com/bufbuild/protovalidate-go |
Replaced by buf.build/go/protovalidate |
github.com/docker/docker |
Split into moby/moby/api + moby/moby/client |
github.com/gregjones/httpcache |
Unmaintained; call site removed |
github.com/grpc-ecosystem/go-grpc-prometheus |
Consolidated into grpc-middleware/providers/prometheus |
github.com/karrick/godirwalk |
Removed |
github.com/libopenstorage/openstorage |
Removed |
github.com/moby/sys/atomicwriter |
Removed |
github.com/mohae/deepcopy |
Removed |
github.com/morikuni/aec |
Removed |
github.com/mrunalp/fileutils |
Removed |
github.com/pkg/errors |
Archived — migrated to stdlib error wrapping |
github.com/zeebo/errs |
Removed |
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp |
HTTP OTLP exporter removed |
go.uber.org/automaxprocs |
Removed |
golang.org/x/xerrors |
Removed (use stdlib errors) |
gotest.tools/v3 |
Removed |
| Module | Before | After | Notes |
|---|---|---|---|
go toolchain |
1.25.0 |
1.26.0 |
Major toolchain upgrade |
google.golang.org/grpc |
v1.72.2 |
v1.80.0 |
+8 minor versions — largest leap |
go.opentelemetry.io/otel |
v1.41.0 |
v1.43.0 |
Core OTel |
go.opentelemetry.io/otel/sdk |
v1.36.0 |
v1.43.0 |
+7 versions |
go.etcd.io/etcd/*/v3 |
v3.6.5 |
v3.6.8 |
All etcd modules |
github.com/google/cel-go |
v0.26.0 |
v0.27.0 |
CEL engine |
github.com/google/cadvisor |
v0.53.0 |
v0.56.2 |
cAdvisor |
sigs.k8s.io/kustomize/* |
v0.20.1/v5.7.1 |
v0.21.1/v5.8.1 |
Kustomize suite |
Vendored Modules: 214 → 208 (−6)
Added (3): cenkalti/backoff/v5, k8s.io/cri-streaming, k8s.io/streaming
Removed (9): armon/circbuf, cenkalti/backoff/v4, gregjones/httpcache,
grpc-ecosystem/go-grpc-prometheus, karrick/godirwalk,
libopenstorage/openstorage, mohae/deepcopy, mrunalp/fileutils,
stoewer/go-strcase
Vendor-only removals (not in go.mod): cenkalti/backoff/v4, stoewer/go-strcase
Green = added, Red = removed, Yellow = version changed.
The full graph shows the characteristic hub-and-spoke structure: k8s.io/kubernetes at the center with its 178 direct edges, surrounded by a ring of staging repos each pulling 60–110 dependencies, and an outer shell of third-party dependencies. The k8s.io staging cluster is densely interconnected but perfectly acyclic — all detected cycles originate in upstream ecosystem packages.
Why-trace targets were selected by fan-in rank (computed from go mod graph) — the packages with the most modules depending on them. Targets exclude golang.org/x/* (toolchain cross-deps) and k8s.io/* (workspace-internal). Pure plumbing (test utilities, protobuf internals) is noted but deprioritized for narrative.
Fan-in rank: #6 | Direct dependents: 80 total (63 k8s.io/*)
go-logr/logr is the structured logging interface used throughout Kubernetes and virtually all of its staging modules. It provides an abstract logging API that decouples logging call sites from the backend implementation — in Kubernetes, klog/v2 serves as the concrete backend. Nearly every k8s.io staging module imports it directly, making it the single highest-fan-in non-plumbing external library in the workspace. It is stable, actively maintained, and has no known replacement.
Fan-in rank: #2 | Direct dependents: 91 total (54 k8s.io/*)
yaml.v3 is on Kubernetes' unwanted-dependencies list — the preferred alternative is sigs.k8s.io/yaml. However, it persists in the workspace graph at 91 fan-in because major transitive dependencies require it: go.etcd.io/etcd/*, go.opentelemetry.io/*, github.com/stretchr/testify, and many gRPC-ecosystem packages all depend on yaml.v3 directly. Eliminating it would require upstream changes across multiple unrelated projects simultaneously. The unwanted-dependencies status correctly flags this as aspirational rather than immediately actionable.
Fan-in rank: #18 | Direct dependents: 48 total (31 k8s.io/*)
gRPC is Kubernetes' core RPC transport. It is used by the API server (aggregated APIs, CRI, CSI, device plugins), etcd client, OTel exporters, and konnectivity. Jumped from v1.72.2 to v1.80.0 (+8 minor versions) in this cycle.
Fan-in rank: #19 | Direct dependents: 48 total (35 k8s.io/*)
OTel is the distributed tracing and metrics framework adopted by Kubernetes for observability. The API server, kubelet, scheduler, and controller-manager all emit spans and metrics via OTel. The high k8s.io/* count (35 of 48) shows how pervasively the OTel instrumentation has been applied across staging modules. Also pulled transitively by gRPC ≥ v1.64 (which bundles OTel as a default dependency for metrics).
Direct dependents: 33 total (27 k8s.io/*)
Prometheus client_golang is the metrics exposition library. All Kubernetes components expose /metrics endpoints in Prometheus format. Also pulled transitively by the gRPC middleware and OTel Prometheus exporter bridge.
Direct dependents: 20 total (19 k8s.io/*)
CEL (Common Expression Language) is the expression engine powering ValidatingAdmissionPolicy, AuthorizationPolicy, and CRD validation rules. It is used only by apiserver-tier staging modules; the low absolute count (20) with high k8s proportion (19) shows it is a first-party Kubernetes dependency with minimal transitive spread.
Direct dependents: 16 total (15 k8s.io/*)
etcd is Kubernetes' primary storage backend. The API server depends on etcd/client/v3 directly; integration test utilities also pull it. The low fan-in (16) reflects that only apiserver-tier modules deal with etcd directly — kubelet, scheduler, and client-facing modules are etcd-agnostic. Updated v3.6.5 → v3.6.8 in this cycle.
github.com/json-iterator/go(archived, medium risk): Production JSON in API server hot paths. Track upstreamencoding/jsonperformance improvements in Go 1.26 as a potential replacement path.gopkg.in/yaml.v3(91 dependents, unwanted): Requires upstream changes in etcd, testify, and OTel. File tracking issues upstream; block new direct uses in Kubernetes code.
github.com/google/gofuzz(archived): Replacementsigs.k8s.io/randfillis in progress. Complete migration in the next cycle.github.com/davecgh/go-spew(105 dependents, unwanted): Pulled by testify. Once testify drops go-spew (tracked upstream), this disappears automatically.github.com/modern-go/{concurrent,reflect2}(55 dependents each, unwanted): Pulled by json-iterator/go. Will drop once json-iterator is removed.
github.com/flynn/go-shlex,github.com/google/btree,github.com/kr/pty: All archived, all low risk. Monitor for supply-chain events but no urgent action needed.- Vendor-only removals:
cenkalti/backoff/v4andstoewer/go-strcasewere removed from vendor but not go.mod — verify these don't re-appear in futurego mod tidyruns.
All commands run from k8s.io/kubernetes at commit d8d43a90343 (Merge pull request #138565 from skitt/drop-otel-semconv-v1.12.0).
depstat version: 45eb8b409b5280a128d6bcbc974952c599954e1d (built from sigs.k8s.io/depstat)
Fan-in ranking command (determines why-trace selection):
go mod graph | awk '{print $2}' | sed 's/@.*//' \
| grep -v '^k8s.io/' | grep -v '^golang.org/x/' \
| sort | uniq -c | sort -rn | head -25Why-trace targets selected (data-driven from fan-in top-25):
github.com/go-logr/logr(80, rank #6)gopkg.in/yaml.v3(91, rank #2 — unwanted, shown to explain persistence)google.golang.org/grpc(48, rank #18)go.opentelemetry.io/otel(48, rank #19)github.com/prometheus/client_golang(33)github.com/google/cel-go(20)go.etcd.io/etcd/client/v3(16)
SVG commands:
# Full graph and diff (awk filter strips depstat diagnostic prefix)
/tmp/depstat graph -d . --svg | awk '/^<\?xml/{found=1} found' > k8s-full-graph.svg
/tmp/depstat diff -d . v1.35.4 HEAD --svg | awk '/^<\?xml/{found=1} found' > k8s-diff-v1354-master.svg
# Why-traces (no awk filter needed — why --svg emits clean SVG)
/tmp/depstat why -d . github.com/go-logr/logr --svg --max-paths 50 > k8s-why-github_com_go-logr_logr.svg
/tmp/depstat why -d . gopkg.in/yaml.v3 --svg --max-paths 50 > k8s-why-gopkg_in_yaml_v3.svg
/tmp/depstat why -d . google.golang.org/grpc --svg --max-paths 50 > k8s-why-google_golang_org_grpc.svg
/tmp/depstat why -d . go.opentelemetry.io/otel --svg --max-paths 50 > k8s-why-go_opentelemetry_io_otel.svg
/tmp/depstat why -d . github.com/prometheus/client_golang --svg --max-paths 50 > k8s-why-github_com_prometheus_client_golang.svg
/tmp/depstat why -d . github.com/google/cel-go --svg --max-paths 50 > k8s-why-github_com_google_cel-go.svg
/tmp/depstat why -d . go.etcd.io/etcd/client/v3 --svg --max-paths 50 > k8s-why-go_etcd_io_etcd_client_v3.svg