Bazel: Fast, Correct Multi-Language Build and Test Tool
What Bazel is
Bazel is an open-source build and test tool in the same family as Make, Maven and Gradle. Its README tagline is "{Fast, Correct} - Choose two", and its pitch is to build and test software of any size quickly and reliably. You describe your project in a high-level build language as libraries, binaries, tests and data, and Bazel works out which compiler and linker invocations are needed, runs them in parallel, caches the results and re-runs only what changed.
Bazel comes from Google. The FAQ describes it as a flavour of the tool Google uses internally (called Blaze, a name that still appears in the codebase) and says it shares most of its code with that internal tool, whose rules are used for millions of builds every day. Google originally built its software with large generated Makefiles, which led to slow and unreliable builds; Bazel was the answer to that. The open-source project supports Java, C++, Android, iOS, Go and many other languages, runs on Linux, macOS and Windows, and is licensed under Apache-2.0.
The FAQ is direct about where Bazel shines: large codebases, projects written in one or more compiled languages, software deployed to multiple platforms, and projects with extensive tests. It is most often adopted by organisations running monorepos with many languages and many engineers, where incremental builds, shared caches and remote execution pay off, but the getting-started tutorials work at the scale of a single hello-world binary.
How it works
Workspaces, packages and targets
A Bazel project lives in a workspace: a directory containing your source files and a MODULE.bazel file at its root, which marks the directory as a workspace and declares external dependencies. Any directory inside the workspace that contains a BUILD file is a package. A BUILD file contains rule invocations, and each one is a target: for example a cc_binary named hello-world built from hello-world.cc, or a cc_library with sources and headers that the binary depends on. Targets are addressed with labels such as //main:hello-world, where //main is the package path relative to the workspace root and hello-world is the target name. Targets declare visibility, so a library can be restricted to the packages allowed to use it.
Build files are written in Starlark, a Python-like language that the docs describe as a subset of Python. Bazel ships built-in rules for common artifact types, and further language support comes from rulesets, which are themselves written in Starlark. Teams can write their own rules and macros the same way.
Load, analyse, execute
Every build or test goes through three phases. Bazel loads the BUILD files relevant to the requested targets, analyses the inputs and dependencies and applies the rules to produce an action graph, then executes the actions until the outputs exist. The action graph records artifacts, the relationships between them and the commands that produce them, which is how Bazel tracks changes to both file contents and commands and knows exactly what needs to be rebuilt. Outputs land in a bazel-bin directory at the workspace root.
Hermeticity and sandboxing
Correctness depends on every action declaring all of its inputs. Bazel runs actions in individual sandboxes and tracks every input file, so the same source code should produce the same binary. The hermeticity guide describes the two aspects as isolation (treating tools as source code and managing them inside the build rather than relying on what is installed on the host) and source identity (identifying inputs by hash). It also lists common sources of non-hermetic builds to clean up before migrating: arbitrary processing in .mk files, tools that embed timestamps or build IDs, system binaries and compilers that differ between hosts, and builds that write into the source tree.
Caching and remote execution
Bazel caches at several levels: in memory, on disk and, optionally, on a remote server. The remote cache stores an action cache that maps action hashes to result metadata and a content-addressable store of output files, so outputs built on one machine or CI runner can be reused by everyone else. Any HTTP/1.1 server that can store opaque bytes can serve as a backend; the docs give nginx with WebDAV, bazel-remote and Google Cloud Storage as examples. A local disk cache works the same way for switching branches on one machine. Beyond caching, Bazel can send actions to a remote build cluster for remote execution, though the FAQ notes that builds run locally by default and a cluster is not required. The "Why Bazel" page says Google routinely sees cache hit rates above 99%.
External dependencies with Bzlmod
External dependencies are managed as Bazel modules. The root MODULE.bazel declares the module and its direct dependencies with bazel_dep, and Bazel looks up the transitive graph in a registry, by default the Bazel Central Registry, before resolving one version per module. Module extensions can define additional repositories and bridge to other package managers, for example to pull Maven artifacts. A lockfile records the resolution for reproducibility and speed. Older projects used a WORKSPACE file instead, which the docs now refer to as a legacy context.
Key features
- Incremental builds. Bazel rebuilds only what changed, based on file contents and command lines rather than timestamps, so there is no need for
make clean. - Parallel execution. Independent actions run concurrently on the local machine or on remote build nodes; the docs say it can run thousands of build or test commands at once when the build structure allows.
- Multi-language builds. C++, Java, Kotlin, Python, Go, Rust and more in a single invocation, so a backend, web UI and mobile app can be built together.
- Multi-platform and cross-compilation. Build for Linux, macOS, Windows and Android from the same
BUILDfiles, with platform and toolchain support for cross-compiling. - Integrated test runner.
bazel testruns only the tests affected by a change, can use remote execution to parallelise them, and can upload results through the Build Event Protocol. - Remote and disk caching. Share action outputs across a team and CI via an HTTP cache, or locally with
--disk_cache. - Remote execution. Offload actions to a build cluster for faster builds and tests.
- Query language.
bazel querytraces dependencies through the build graph. - Extensibility in Starlark. Write custom rules and macros, and reuse community rulesets for languages and frameworks.
- Bzlmod and the Bazel Central Registry. Versioned modules with deterministic resolution and a lockfile.
Getting started
The install docs recommend Bazelisk on Ubuntu, macOS and Windows. Bazelisk is a launcher that downloads the right Bazel version automatically, builds with the version pinned in a .bazelversion file (which you check into version control for reproducibility), and can track the latest LTS or rolling release. Package-manager installs are also documented. On macOS with Homebrew:
brew install bazel
bazel --version
On Ubuntu, after adding Bazel's APT repository and signing key as described on the install page:
sudo apt update && sudo apt install bazel
Bazel bundles its own JRE, so no system Java is needed to run it; you only need a JDK if you build Java code. The APT repository has no arm64 build and there is no binary installer for Arm Linux, so Arm users should use Bazelisk or compile from source. Community-maintained packages exist for Alpine, Arch, Debian, Fedora, FreeBSD, Nixpkgs, Scoop and others, and the team notes it does not officially support those.
The C++ tutorial is the quickest way to see the workflow. Clone the examples repository and build the first stage:
git clone https://github.com/bazelbuild/examples
cd cpp-tutorial/stage1
bazel build //main:hello-world
bazel-bin/main/hello-world
The BUILD file in the second stage shows a library and a binary that depends on it:
cc_library(
name = "hello-greet",
srcs = ["hello-greet.cc"],
hdrs = ["hello-greet.h"],
)
cc_binary(
name = "hello-world",
srcs = ["hello-world.cc"],
deps = [
":hello-greet",
],
)
External dependencies are declared in MODULE.bazel, as in this example from the external dependencies overview:
module(name = "my-module", version = "1.0")
bazel_dep(name = "rules_cc", version = "0.1.1")
bazel_dep(name = "platforms", version = "0.0.11")
To share build outputs between branches on one machine, the remote caching docs show enabling a disk cache in .bazelrc:
build --disk_cache=path/to/build/cache
Tutorials for Java, Android and iOS follow the same pattern.
Use cases
- Polyglot monorepos. One build system and one dependency graph for C++, Java, Go, Python and front-end code in a single repository.
- Shared CI caching. A remote cache lets CI runners and developers reuse each other's outputs, so a change rebuilds only the affected targets. The FAQ advises not cleaning before CI runs, because Bazel does not need clean builds to be correct.
- Large C++ and Java codebases. The core C++, Java and shell rules are the most heavily used inside Google and, per the FAQ, have very little churn.
- Mobile and multi-platform products. Build Android and iOS apps alongside the servers they talk to, with cross-compilation for other targets.
- Reproducible artifacts for deployment. Statically linked C/C++ binaries and self-contained Java jars that the FAQ notes are simple to put in containers, plus rules for building container images.
- Speeding up big test suites. Run only affected tests, in parallel, and use remote execution to rerun a flaky test many times to detect flakiness early.
How it compares
The docs compare Bazel mainly with Make, Maven, Gradle and Ant. Against Make, the FAQ's point is the level of abstraction: with Make you describe individual files and compiler invocations, while Bazel's BUILD language describes a project as small interconnected libraries, binaries and tests, each declaring its direct dependencies completely. That dependency information is what allows correct incremental and parallel builds. Against language-specific tools such as Maven and Gradle, Bazel's advantage is building many languages in one invocation and one graph, at the cost of leaving each ecosystem's native conventions.
The FAQ also addresses Docker as a reproducibility tool: containers pin the system environment, such as which compiler is installed, but do not make an imperfect Makefile produce predictable results when the source changes. Bazel targets reproducibility of the build itself, and the two can be combined.
Things to know before adopting
- Not for uncacheable work. The FAQ lists what not to run through Bazel: compilation steps that fetch data from the internet, tests that hit a live QA instance, and deployment steps that change cloud configuration. Builds made of a few long sequential steps also gain little unless broken into smaller targets.
- Reproducibility needs discipline. Java and C++ builds are reproducible if you keep the toolchain fixed, but custom rules must avoid undeclared dependencies, timestamps and user IDs in outputs, network access and randomness. Sandboxed execution helps find undeclared dependencies, and the FAQ notes the strict sandboxed spawn strategy is Linux-only.
- Release tracks and upgrades. Bazel has rolling releases about every two weeks and an LTS major release roughly every 12 months; each major version is an LTS. Per the release page, Bazel 9 is the active LTS (9.3.2, supported until December 2028), Bazel 8 and 7 are in maintenance, and Bazel 10 is in rolling pre-release. Breaking changes between majors come with
--incompatible_*flags and migration notes, and pinning with.bazelversionplus Bazelisk keeps teams on the same version. - Migration effort is real. Moving an existing project means writing
BUILDfiles with complete dependency declarations, adopting rulesets for each language and fixing non-hermetic steps first, which the hermeticity guide recommends doing before switching. - Secure your remote cache. The cache holds your binaries, so the docs say it needs to be secured; the example nginx setup still requires adding authentication.
- Known cache pitfalls. Modifying input files during a build can upload invalid results to a remote cache, and environment variables can leak into actions; the remote caching page documents both.
- Platform notes. The Ubuntu page lists 18.04, 20.04 and 22.04 LTS as supported and says other Ubuntu and Debian releases are untested. Older macOS binary installers must be downloaded with
curlbecause of signing requirements. - Not everything is developed in the open. The FAQ says interfaces between public Bazel and Google's internal extensions are refactored frequently, so some changes appear without public discussion, and some Google-specific code will never be open-sourced.
Project activity
As of November 2026 the bazelbuild/bazel repository has about 26,000 stars and around 4,670 forks on GitHub. It was created in June 2014, is written mainly in Java and is licensed under Apache-2.0. The latest release at the time of writing is 9.3.2, published on 5 November 2026, and the master branch receives commits daily. Security issues go to the address given in the README, with a stated 90-day disclosure timeline.
The source is at github.com/bazelbuild/bazel, and documentation, tutorials and the rule reference are at bazel.build.
Enjoying this project?
Discover more amazing open-source projects on TechLogHub. We curate the best developer tools and projects.
Repository:https://github.com/bazelbuild/bazel
GitHub - bazelbuild/bazel: Bazel: Fast, Correct Multi-Language Build and Test Tool
Bazel is Google's open-source build and test tool, licensed Apache-2.0. It builds C++, Java, Go, Android, iOS and more from Starlark BUILD files, rebuilding onl...
github - bazelbuild/bazel
Related Projects
Maintain Bazel: Fast, Correct Multi-Language Build and Test Tool? Add the badge to your README
Show that the project is featured on TechLogHub and send readers to this write-up.
[](https://techloghub.com/open-source/bazel?utm_source=techloghub_badge&utm_medium=referral&utm_campaign=readme)

