Hermetic, Ephemeral, Reproducible Builds: Take Three (1)

2025/10/06

This is another piece of news in my quest for hermetic, ephemeral, and reproducible builds (“HER”). If you read my articles in the past months or so, you may have noticed that I am looking for good ways of creating completely hermetic, ephemeral, reproducible, but also practical builds.

In the past, I offered two approaches to HER builds that I believe take a stab at providing HER properties, while also remaining practical to everyday build engineers. I’ll discuss them below, as well as a proposal for improving them.

Before that, let’s reiterate some definitions first, so we know what problem we’re trying to solve.

Definitions

This is adapted from an earlier article for convenience.

Hermetic

A build is said to be hermetic, if each build step only has access to the set of input artifacts that are minimally required for the build to succeed.

Ephemeral

A build is said to be ephemeral, if the build relies only on the tooling that the build process installs or builds itself.

Reproducible

A build is said to be reproducible, if two identical instantiations A and B of the same build process P produce bit-identical artifacts.

Practical

A build is said to be practical,if its setup does not require extreme adaptation of the build process to satisfy any of the previous properties.

Prior art

This is a short inventory of prior art intended to contrast conventional approaches to building software with HER-oriented build processes. I am rushing through the history of build systems to get to

Make builds

I use the term “make builds” for all “conventional” build approaches. This includes the original make, its cousins and descendants, today most typically GNU Make, and similar utilities, such as cmake, ninja, gn, scons and others, including the clever hacks do, redo and similar.

All the above focus on building software artifacts (most immediately C and C++ programs), assuming an ambient development environment which is provided outside of the build process.

Container builds

These are builds which use the hermeticity of Docker and similar confined environments.

Self-sufficient builds

Self-sufficient builds are builds with partial or full provision of development environment, such as one can achieve using bazel, buck{,2}, pants and similar.

These builds do a partial to full setup of the build environment. However, full HER builds are not a given: usually involved bespoke setup is required to bring in all the needed dependencies in a HER manner.

The role of Bazel

Bazel is my tool of choice for achieving HER builds. I am sure that with the right amount of care anything I do here can be achieved with any of the tools mentioned above. However, I chose Bazel for somewhat subjective reasons, which I believe still to be good:

My work on HER builds

As hinted in the intro, I provided two approaches that to an extent provide the ideals espoused here. These are, ordered from older to newer:

Both approaches work, and I use them today for various parts of my workflow. I had a few years of running both of them side by side, depending on the project in question and the sophistication of the setup required. However, during that time, I also noticed some issues which threaten the longevity of both approaches.

BID

Pros

Cons

N+B

Pros

Cons

Enter Bazel Modules

Despite the shortcomings listed above, I was quite pleased with the dev setups I achieved using the above two techniques. I currently use a combination of both as appropriate. I enjoyed the setup I had, until it became obvious that a migration from Bazel workspaces to Bazel modules was imminent. At this moment, Bazel 9 which is scheduled to be released in late 2025 will completely disable the use of workspaces.

This means, anyone who wishes to keep up with the latest features of Bazel (which should be most of us), will want to migrate their setups to Modules.

The upside of Modules is the promise of greatly simplified dependency management. The downside of Modules is the required “boil the ocean” migration, and the introduction of the Bazel Central Registry (“BCR”). In theory, this is the place where you would be able to get the latest and greatest of all the dependencies you need.

In practice, however, someone has to build this registry out, and that one is you (among others). With a sufficiently niche interest, chances are that BCR will not have what you need. In the best of worlds, if your work has a unique value proposition, chances are that you will need something that nobody else has needed before. At which point, it will be up to you to create it. Unfortunately, due to how open source software builds on itself, this means that you might find yourself in a situation where you need to unravel your dependencies all the way to the very basic of modules. And most likely this means creating them yourself.

Congratulations, you are now a software repository manager.

As luck would have it, both of my approaches, BID and N+B are somewhat annoying to port, so much so that instead of making that change, I started thinking how to not have to do it at all.

Which led me to the natural next step… stay tuned.