Moving a Bazel project from WORKSPACE to Bzlmod swaps hand-written download rules for modules resolved from a registry, with a deterministic version choice and a lockfile. That helps visibility and repeatability, but it does not vet the modules for you, so you still own review.
Definitions first
A Bazel module is a Bazel project that can have multiple versions, each publishing metadata about the modules it depends on. Its manifest is a file named MODULE.bazel at the repository root, declaring the name, version and direct dependencies. The older WORKSPACE approach instead listed each external repository, usually with a URL and checksum, by hand.
Bazel resolves modules by reading the root MODULE.bazel, then requesting each dependency's MODULE.bazel from a registry until the whole graph is known.
What improves
| Area | WORKSPACE style | Module style |
|---|---|---|
| Transitive dependencies | You list and order them yourself | Resolved from registry metadata |
| Version choice | Whichever definition is loaded first | Minimal Version Selection, highest requested version wins |
| Visibility | Read the file | Query the graph with the mod command |
| Repeatability | Checksums you remembered to add | Lockfile of resolution results |
| Direct access | Any repo name is visible | A module sees only its direct dependencies |
Two details are worth stating carefully. Minimal Version Selection picks the highest version any dependent asks for, not the newest one that exists, which keeps resolution high fidelity and reproducible. And because a module can only find its direct dependencies, accidental reliance on a transitive package is blocked by default.
What does not change
- You still trust the registry and each module author. Registry metadata says where source comes from, not whether the source is safe.
- Overrides still exist. Only the root module's overrides take effect, but they can point a transitive dependency at an archive, a git commit or a local path, bypassing registry versions entirely.
- Module extensions can run arbitrary code at fetch time, including file reads and network requests. Review them like build scripts.
- Yanked versions are an error by default, but a flag can allow them.
A migration checklist
- Move each direct external into a bazel_dep entry with an explicit version.
- Replace archive fetches with modules where the registry has them. Keep checksums on anything you still fetch directly.
- Commit the lockfile and decide how CI treats drift (see the companion post on lockfile modes).
- Search for override entries and write down why each exists.
- Read every module extension you depend on, including those pulled by dependencies.
- Record the resulting graph as an inventory you can scan.
Bazel also ships a migration guide that walks through this conversion, and the mod command can print the resolved graph while you work.
Where Safeguard fits
Safeguard can inventory the dependencies your repository declares through SBOM and dependency scanning, and reachability analysis helps you decide which findings matter first. You can try a free scan on a repository to see what it finds. Check coverage for your specific build files before relying on it.
Try it yourself
Open the demo and compare the direct dependencies with the full inventory. A resolved module graph has the same shape, with more nodes than you declared.
Click through it at your own pace, or open it full screen.
Quick check
Does Bzlmod verify that a module is safe?
No. It resolves versions and records hashes of registry files. It does not review the module's source for malicious behavior.
Do overrides in a dependency's MODULE.bazel apply to my build?
No. Only the root module's overrides take effect. A dependency's overrides are ignored.
Sources
- Bazel documentation: Bazel modules
- Bazel documentation: Bazel lockfile
- Bazel documentation: Module extensions
Last reviewed: 19 October 2026.