Bazel 常被概括成一件很快的构建工具,这句话当然没有错,问题在于它把真正的架构藏起来了。速度确实存在,却属于后果。Bazel 把构建理解成一张由声明式 action 组成的图:每个 action 都要先把输入、输出、命令行和环境约束讲清楚,然后系统才去判断哪些工作能够复用,哪些结果能够被别的机器信任。[1][3][5] 也正因为这样,Bazel 的价值超出“把编译跑快一点”。它更像一套把构建工作整理成可缓存、可检查、可迁移单元的系统,让正确性先成形,性能再跟上来。

截至 2026-05-05T01:34:37Z UTC,GitHub API 显示 bazelbuild/bazel25,354 个 stars、4,460 个 forks、1,883 个 open issues,最近一次 push 时间是 2026-05-05T01:13:15Z。[6] 当前最新稳定版是 9.1.0,发布时间为 2026-04-20。[7] 这些数字本身不能替任何团队做工具选择,它们却说明了一件更基本的事:Bazel 作为构建系统,仍然处在大规模、持续运转的工程表面上,支撑它的力量来自持续使用与工程约束。对 Bazel 来说,这一点很重要,因为它的收益只会落在愿意接受那些硬约束的团队身上,旁观式的性能崇拜吃不到这部分回报。

图片说明:题图使用的是英国国家档案馆服务器机房的真实照片,避开 logo 与抽象“流水线”意象。它和 Bazel 的贴合之处在于,Bazel 的架构只有在 action 输出离开本机上的一次性副产品身份,开始变成可以在多台机器之间反复复用的基础设施之后,才会完整显形。[8]

真正的单位是 action,target 只是请求入口

Bazel remote-caching 文档里有一句重要的话:Bazel 会把构建拆成离散步骤,这些步骤叫作 actions;每个 action 都带着输入、输出文件名、命令行与环境变量。[3] 这句话几乎已经把整套系统的骨架讲完了。target 是开发者发起请求时看到的表面单位,真正被调度、被缓存、被复用的,是下面那一层 action。

aquery 文档把这件事说得更直白。这个命令存在的目的,就是让你在 analysis 之后,直接去看 build graph 里的 actions、artifacts 以及它们之间的关系,还能看到系统最后生成出来的命令、输入、输出和 mnemonics。[5] 这重要,因为 Bazel 没有要求你相信一种模糊的“增量构建魔法”。它给了你一套办法,去检查系统自己相信的那张图到底长什么样。对 Bazel 来说,性能不只是并行度的副产品,它来自更强的结构声明,让这张图可以被推理、被缓存、被调试。

这也解释了 Bazel 为什么在早期总显得比普通 task runner 更重。你需要更早地把信息交出来,因为 Bazel 想做的事情,是把构建过程从一串临时 shell 动作,提升成图形化、可描述的数据。[3][5] 它给你的回报,是后续系统终于有能力判断哪些东西可以安全跳过、哪些结果可以安全复用、哪些动作必须重跑,抽象的优雅只在其后。

Hermeticity 是整套信任契约

Bazel 的 hermeticity 文档把目标定义得很清楚:同样的源输入,加上同样的产品配置,hermetic build system 应当返回同样的输出。[1] 这句话背后拆成两层。第一层是 isolation,也就是工具与依赖要被当成受管理的输入,宿主机里飘着的环境状态不能充当隐形前提。第二层是 source identity,也就是系统需要稳定地识别,到底是哪一部分输入发生了变化。[1]

Sandboxing 是 Bazel 在执行阶段逼近这份契约的做法。文档写得很明确,Bazel 会在一个只包含已知输入的工作目录里运行进程,从而避免工具顺手读取那些并未声明的文件。[2] 在支持 user namespaces 的平台上,进程也无法修改工作目录之外的文件。[2] Bazel 没有假装宿主环境彻底消失,它做的是把那些隐藏依赖与越界写入逼到光下,让执行形状变得更老实。

这层约束指向的是可验证性。Sandboxing 文档明确指出,若没有 action sandboxing,Bazel 就无法知道某个工具是否偷偷用了未声明输入;一旦这些输入改变,系统仍会误以为构建结果是最新的,于是错误的增量结果就会被当成正确答案留下来。[2] 到这里,Bazel 的架构逻辑就突然变得很完整了。Hermeticity 的价值落在后续所有承诺的成立条件上。

Remote cache 把正确性变成团队层的资产

Remote cache 让 Bazel 从一台机器上的优化,转成团队范围内的系统。文档说得很直接:remote cache 由开发者与 CI 共同使用,只要构建足够可复现,一台机器生成的输出就可以被另一台机器安全复用。[3] 同一页还说明,remote cache 里存的是两层东西:一层是 action cache,把 action 哈希映射到结果元数据;另一层是 content-addressable store (CAS),保存真正的输出文件。[3]

这组细节的重要性在于,它说明 Bazel 的速度叙事从来就超出纯粹算力叙事。只要 action 描述完整、输入边界真实,另一台机器就能拿着同一个 action key,相信那个结果,省下重跑的成本。反过来看,若 action 背后藏着宿主机私货、未声明工具链或环境偏差,cache 就会从加速器滑向错误结果的分发渠道。[2][3]

Sandboxing 文档其实已经把这层风险点破了:错误复用 cache entry 会影响整支团队,而 shared cache 一旦被污染,清空整个缓存无法成为可持续的解决方法。[2] 所以“Bazel 很快”只是表层描述,更准确的话是:Bazel 试图把构建正确性做得足够可携带,快这件事才有资格被共享。

Remote execution 顺着同一套约束往外走

Remote-execution 文档给出的下一步也很自然:默认情况下,Bazel 在本地机器上执行 builds 与 tests;remote execution 则把这些 build/test actions 分发到多台机器上,例如一个 datacenter。[4] 文档给出的收益包括三件事:更高的并行度带来更快的执行速度、更一致的团队执行环境,以及跨团队复用构建输出。[4] 同一页还写明,Bazel 为 remote execution 和 remote caching 使用的是一套开源 gRPC 协议。[4]

放在架构层面上看,remote execution 只是前面那份约束的自然外延。Sandboxing 文档说得很清楚:若一个构建在 sandboxing 下工作得好,它通常也更容易在 remote execution 下工作得好,因为这整套机制的目标,本来就是让 action 依赖被描述得足够明确,使另一台机器也能接手执行,而不需要继承本地那些说不出口的前提。[2] 也就是说,remote execution 属于 action graph 严格到可以离开笔记本之后自然长出来的形态,距离后来外挂的一档性能模式很远。

这也是很多 Bazel 迁移最容易被讲歪的地方。团队常常盯着 remote execution 这个 headline feature,然后才发现真正艰难的工作都发生在更前面:清理 undeclared inputs、收紧 toolchain、去掉 timestamp 泄漏、让 action 足够可读,使 cache key 真正代表同一件事。[1][2][4] Bazel 当然能让 build farm 变得有用,只是它不愿意站在一套松垮契约上去兑现这个结果。

Bazel 适合什么地方

当一个团队已经大到需要把构建正确性变成共享基础设施时,Bazel 会显得很有力量。Polyglot monorepo、大规模 CI、想让多台开发机稳定吃到同一批 cache hits 的组织,都更容易从它身上得到真实回报。[3][4] 原因超出性能本身,落在 Bazel 提供的一种办法:让构建行为在 action 边界上变成可检查、可约束的对象。[2][5]

它的边界也同样清楚。若代码库很小,toolchain 很窄,本地构建早就够快,Bazel 的显式性就更像负担,解脱感来得很晚。它要求你先为声明式结构付费,回报则出现在后面:当 cache reuse、remote execution 与 reproducibility 的重要性,开始压过本地便利时,那些前置成本才会一起回到你手里。[1][3][4]

所以理解 Bazel,先该把它当成一篇架构笔记来读,benchmark 排行只能看见较薄的一层。图先成立,hermeticity 护住这张图,sandboxing 检验这张图,remote cache 共享图的结果,remote execution 把图送到更多机器上。速度只是这些层同时对齐以后,自然露出来的表面。[1][2][3][4][5]

来源

  1. Bazel 文档,《Hermeticity》—— hermetic build 的定义、isolation、source identity,以及可复现性与并行动作图为何依赖这些前提。
  2. Bazel 文档,《Sandboxing》—— 只含已知输入的工作目录、execroot、未声明输入风险、shared cache 污染后果,以及 sandboxing 与 remote execution 的关系。
  3. Bazel 文档,《Remote Caching》—— action 模型、声明式输入输出、remote cache 的工作流,以及 action cache 与 CAS 的区分。
  4. Bazel 文档,《Remote Execution Overview》—— 本地与远端执行模型、团队层收益,以及 remote execution / remote caching 所使用的开源 gRPC 协议。
  5. Bazel 文档,《Action Graph Query (aquery)》—— analysis 之后如何查询 actions、artifacts、命令与 action 关系。
  6. GitHub API,bazelbuild/bazel 仓库快照——仓库描述、stars、forks、open issues、默认分支与写作时点的最近 push 状态。
  7. GitHub 上 bazelbuild/bazel9.1.0 发布页——本文采用的最新稳定版时间锚点。
  8. Wikimedia Commons,《File:A view of the server room at The National Archives.jpg》——本文题图所用真实服务器机房照片来源页。