Bazel forces your build targets to be hermetic - you must declare all your inputs (source or generated files), else you just won’t see them in the sandbox (and, if you really need them, you will fail).1
When I had to port some stuff from Maven to Bazel a few months ago, I immediately hit that wall (like everyone else, it seems). That sounded like an odd quirk. Why all that strictness? It’s just so painful.
Now I realize it’s all about having a strict build graph. Hermeticity allows Bazel to “know” which depends on what. That’s unlike every other build system which I’ve ever used.
So what’s the big deal? Consider the following common situations.
Who broke this FooTest? Easy. With some query magic you can find which recent commit touched anything that might have effect on that particular test. You can even write your own automated breakage culprit finder.
Can I safely remove this target? Can I be certain without re-building the world? Easy. Query for usages again.2
Have you tried parallel Maven builds with
mvn -T? You probably know all the pain of having “race conditions” between the builds of your modules and seeing random failures in a tiny percent of your builds.3
That’s gone with Bazel. By knowing your build graph well and being 100% sure what depends on what, it knows which things can be built in parallel.
The more general implication of all that hermeticity is that it allows you with means of encapsulation of your build targets - you can hide the internals and be sure that nobody can implicitly depend on your targets.
That gives you power to iterate on and refactor targets way more confidently.
Caching & remote execution
Last but not least, hermeticity guarantees you can do safe caching. Just combine the hashes of all inputs and that’s your cache key.4
Same for remote build execution - just pack the inputs and pass them to the remote worker.
This is a big deal for any larger application, where you don’t want to wait for a full rebuild for most of the changes.
While not a panacea (sorry, build engineers!), hermeticity is a great leap in the maintainability of builds.
This is one of the really good things Bazel brings to the table and a core tenet enabling most of its great features. While hermeticity can’t easily be added to older build systems (everything will explode!), I hope it becomes a norm for future tooling.
Local builds (i.e. ones with the
localflag) are an exception because sandboxing is disabled, but that’s another story. ↩
Actually, Bazel will most likely break in the analysis phase if you remove something with remaining usages. ↩
I know this can be solved by organizing your project well, being strict, using plugins, etc. But for a non-trivial project that requires a lot of extra effort and still leaves plenty of room for error. ↩
Same disclaimer for local builds - they’re an exception. ↩