Published 25 aug. 2024

Adopting Bazel

#CI/CD
#rust
#docker
Cover image for Adopting Bazel

What is Bazel?

Bazel is a free, open-source tool for automating software builds and tests, based on Google’s Blaze. Released in March 2015, it uses Starlark, a Python dialect, for defining build rules. Bazel supports multiple languages, including Java, C++, Python, Go, Rust, and JavaScript.

Why Bazel?

Requirements

We primarely use Rust but if we look at the software we’re maintaining it’s a mixture of Rust, Go, Python and Typescript. In most cases we will use a microservice based architecture in a monorepo where services will be written in either Go or Rust. This also means that any build tool we pick should support multiple programming languages.

Aditionally, it should be correct and fast. We’ve had our fair share of issues related to dependency management (it works on my machine!) and slow builds related to our docker setup. In some cases it could take 10-20 minutes to build one of our projects when one of the core libraries was updated.

To sum it up:

Multi-language, correct and fast

Our Journey

We experimented with moon, turbo repo, Earthly and others but none of them felt right. Most tools didn’t support reproducible builds, multi-language support or were not widely adopted.

Finally we stumbled upon Bazel. The fact that is was open-source and widely adopted by companies like Google, Uber, Dropbox and others gave us some confidence although it was clear from the beginning that it had a steep learning curve.

After a few weeks of experimenting we got our first (tiny) project to build and run with Bazel using rules_rust and rules_oci. We eventually packaged libpq for Bazel which is required when using diesel with PostgreSQL.

The good

Instead of saying “well, you need to follow these 28 steps first” we can now say “just run bazel run //my-service”. It simply worksTM. It’s fast too and for most projects we no longer need to install any tools (not even Docker!).

We’ve also adopted BuildBuddy which uses the Build Event Protocol to visualize the build process and supports remote execution.

BuildBuddy Timings Interface

BuildBuddy Timings Interface

The bad

Bazel’s cross-platform build system promises uniformity, but setting up a new module can be surprisingly time-consuming. While Bazel excels at reproducible builds, integrating essential dependencies like OpenSSL often requires careful configuration of build rules and external repositories.

The complexity compounds when dealing with native cross-compilation, where platform-specific challenges – such as Mac’s unique linker behavior – can introduce unexpected build failures that require careful troubleshooting.

IDE integration

This remains one of Bazel’s weak spots. While plugins exist for major IDEs like IntelliJ and VS Code, they often struggle with sync failures, dependency resolution, and sluggish performance. The fundamental mismatch between Bazel’s project model and traditional IDE expectations means developers must often rely on the command line for building and testing.

Conclusion

We got to be honest, the investment in Bazel was huge and it’s definitely not for everyone. It also didn’t help that bzlmod (Bazels version of proper module management) was still in beta and most rules didn’t support it yet. But, for us it was definitely worth it. Why? Well, let me give you two examples:

1. No more “it works on my machine”

We have a complex project that uses a multitude of third-party dependencies (docker, protobuf, open policy agent, rust, etc.). Without installing any tools (not even Docker!) our interns were able to build and run the project in less than 15 minutes.

2. Fast incremental builds

In our previous setup with Docker it could take up to 20 minutes to build the project when something was changed in one of the shared crates. This happened because the build cache was invalidated forcing cargo to basically build from scratch.

In Bazel, each individual target is cached and only invalidated when the inputs change. Whenever we change a shared crate, only the dependent targets are invalidated and rebuilt.

We specialize in delivering cutting-edge solutions.

Contact

Address

Rodetorenplein 15

8011 MJ, Zwolle

Nederland