Skip to content

Back to the catalog

5 Tips for Managing Bazel Dependencies (Without Losing Friends) | Endor Labs
endorlabs.comarticle

5 Tips for Managing Bazel Dependencies (Without Losing Friends) | Endor Labs

Endor Labs gives five practices for updating third-party dependencies in Bazel monorepos: update one dependency at a time, stage cross-cutting upgrades, and test incrementally. It includes bazel query rdeps examples (e.g. for the Go zap library) to select only affected go_test targets. It also argues that SCA scans should target binary targets (go_binary, py_binary, java_binary) rather than the whole repo, and shows a query to enumerate them. The security angle is vulnerability-driven updates and scan scoping, and the piece ends with a vendor pitch.

TL;DR Endor Labs' tips for safe dependency updates in Bazel monorepos, with bazel query recipes for impacted-test selection and binary-target-focused SCA scanning.
Document

What it is

Endor Labs' guidance on updating third-party dependencies in a Bazel monorepo. Its useful content is a set of bazel query recipes for narrowing test and scan scope. The rest is process advice and a vendor pitch.

How it works

Bazel pins one version of each dependency for every target, so a bump is visible repo-wide at once. The page treats that as a benefit: the person making the update owns every breakage it causes, and nothing ships broken into another team's repo. The recipes below rely on the same graph visibility to limit what gets tested or scanned.

What to do

Test only the consumers of a bumped library. The page's example is zap in src/golang/:

bazel query 'kind("go_test", rdeps(//src/golang/..., @org_uber_go_zap//:zap))'
bazel test $(bazel query 'kind("go_test", rdeps(//src/golang/..., @org_uber_go_zap//:zap))')

This covers direct and transitive consumers. To cut CI time further, cap the reverse-dependency depth:

bazel test $(bazel query 'kind("go_test", rdeps(//src/golang/..., @org_uber_go_zap//:zap, 2))')

The page says depth limiting suits minor patch or non-breaking changes, where deeper transitive paths are unlikely to be affected.

Enumerate the binary target types to scan.

bazel query 'kind(".*_binary", //...)' --output=label_kind \
| awk '{print $1}' \
| sort -u

This lists the rule kinds present, such as go_binary, py_binary and java_binary. The page recommends pointing SCA at binary targets rather than the whole repo. A binary scan covers first-party libraries and all transitive third-party dependencies of the shippable artifact. The stated payoff is that it ignores dependencies not used by what you deploy, and it makes per-target scans fast enough to run on each change instead of nightly.

Process rules:

  • Update one dependency at a time, so a failure can be traced to a single change and reverted cleanly.
  • For cross-cutting libraries such as protobuf, stage the work as a series of scoped PRs to a branch, one set of consumers at a time.
  • Consider making central dependency updates a platform or DevOps team responsibility at large scale.
  • In CI, scan only targets affected by the change, not every target on every run.

Caveats

  • In a small repo the page says to skip the query tricks and run all tests.
  • Unit tests are the first line of defense and run locally. Integration tests often need CI-only environments.
  • Depth-limited rdeps trades coverage for speed. The page gives no guidance on choosing depth beyond "minor patch".
  • The page asserts that binary-target scanning cuts noise, but offers no benchmarks or numbers.
  • The "affected targets only" scan strategy is described in general terms, with no command or tooling shown. The closing pitch is for Endor Labs' Bazel-native component discovery.

Technologies named here

Techniques named here

  • Incremental testing using reverse dependencies