on
Bazel: Love it or Leave it? My Short Experience
For the past two years, I’ve been using Bazel, an open-source build tool similar to Make, Maven, and Gradle. As their documentation states: “Bazel is an open-source build and test tool similar to Make, Maven, and Gradle. It uses a human-readable, high-level build language. Bazel supports projects in multiple languages and builds outputs for multiple platforms. Bazel supports large code-bases across multiple repositories, and large numbers of users.”
My experience has primarily been within a monorepo setup, managing code written in both Java and Golang. While I wouldn’t call myself a Bazel guru, I’ve become comfortable with the core functionalities:
-
Building Targets: The build command, as the name implies, is used to construct specific targets within the project.
-
Running Tests: The test command kicks off the execution of tests.
-
Syncing Changes: The sync command ensures my local Bazel environment reflects any recent modifications, such as new dependencies pulled from a remote repository.
The good
- Universal Commands: Bazel offers a consistent approach regardless of the
programming language (Java, Go, etc.). Building something is always done
with
bazel build //some-target
, while running tests usesbazel test //some-target
. This uniformity simplifies your workflow. - Blazing Fast Builds: Bazel excels at building projects efficiently. Its secret sauce for achieving this speed remains under the hood, but the results are undeniable.
- Effortless Target Creation: Defining new targets for specific tasks, like executing a CI request, is remarkably easy. This allows you to tailor Bazel to your specific needs.
- Familiar Ground for Make Users: The concept of “targets” draws inspiration from Make, providing a familiar foothold for those already comfortable with that tool.
- Powerful Dependency Exploration: The
query
command serves as a powerful tool for navigating complex codebases. It helps you uncover dependencies within packages (who’s using them) and identify potential conflicts. While not an everyday task, it’s invaluable for large repositories. - Sandbox Magic: Bazel’s sandboxing feature ensures a consistent environment across machines. This allows for reliable builds and execution, regardless of the system’s configuration.
The bad
-
Resource Intensity: Bazel can be a CPU hog, especially during tasks like
bazel sync
in large repositories. This can manifest as loud fans, lagging performance, and a generally unresponsive system. While multi-core usage is configurable, the options may not be immediately intuitive. -
Black Box Build Language: Bazel’s high-level language abstracts away the underlying processes. This can be beneficial for simplicity, but the lack of transparency makes troubleshooting issues challenging. If something goes wrong, deciphering the cause can be difficult.
-
Golang Integration Hurdle: Native Golang support isn’t built-in.
Gazelle
[4], a plugin acting as an intermediary, adds complexity and extra steps for Golang users. This can be disruptive for those accustomed to a streamlined Golang workflow.
By acknowledging these drawbacks, we can make informed decisions about whether Bazel is the right fit for a particular project.
And the ugly
-
Integration Challenges: Bazel’s support for IntelliJ, a popular IDE for Java developers, is less than ideal. Frequent updates to Bazel, IntelliJ, or the Bazel plugin can introduce compatibility issues, disrupting your workflow. This necessitates a cautious approach to updates, which can be frustrating.
-
Version Lock vs. Updates: Bazelisk helps manage this complexity by enforcing specific Bazel versions. However, this approach can create a version lock situation for IntelliJ and its Bazel plugin as well. While sticking to stable versions minimizes breakage, it also means missing out on potential improvements in newer releases. The ideal scenario would be a world where frequent updates don’t disrupt the development process.