开发者工具演进史
2026/3/22
开发者工具构建系统DevOps
开发者工具演进史
从 Make 到 Bazel 再到 Buck2,开发者工具的 50 年演进。
第一代:1970s-1980s - Make
Make
# Makefile(1976)
all: program
program: main.o utils.o
gcc -o program main.o utils.o
main.o: main.c
gcc -c main.c
utils.o: utils.c
gcc -c utils.c
clean:
rm -f *.o program
特点:
- 简单:声明式,易于理解
- 依赖管理:文件时间戳自动构建
- 跨平台:Unix、Windows、macOS
局限:
- 增量构建弱:难以精确追踪
- 并行构建差:依赖关系处理笨
- 语言无关:不识别 C、Java、Python
第二代:1990s - Ant, Maven, Rake
Ant(Java)
<!-- build.xml(2000)-->
<project name="myproject" default="compile">
<target name="compile">
<javac srcdir="src" destdir="classes"/>
</target>
<target name="run" depends="compile">
<java classname="MyClass" classpath="classes"/>
</target>
</project>
Maven(Java)
<!-- pom.xml(2004)-->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>myapp</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.0</version>
</dependency>
</dependencies>
</project>
Rake(Ruby)
# Rakefile(2003)
task :default => [:build]
task :build do
sh "gcc -o program main.c utils.c"
task :test => [:build] do
sh "./program test_input"
end
特点:
- 语言感知:Java、Ruby 专用
- 依赖管理:Maven Central、RubyGems
- 任务系统:Ant targets、Rake tasks
局限:
- XML 繁琐:Ant 的 XML 配置冗长
- 全局锁:Maven 的单一版本依赖
- 缺乏增量:仍然不精确
第三代:2000s-2010s - Gradle, SBT, Leiningen
Gradle(JVM)
// build.gradle(2008)
plugins {
id 'java'
}
dependencies {
implementation 'org.springframework:spring-core:5.3.0'
testImplementation 'junit:junit:4.13.2'
}
tasks.named('test') {
useJUnitPlatform()
}
SBT(Scala)
// build.sbt(2008)
name := "myproject"
version := "1.0"
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.0" % Test
)
Leiningen(Clojure)
;; project.clj(2009)
(defproject myproject "1.0.0"
:dependencies [[org.clojure/clojure "1.11.0"]]
:plugins [[lein-cljsbuild "1.1.7"]])
特点:
- DSL 友好:Groovy、Scala、Clojure
- 依赖解析灵活:冲突解决更聪明
- 增量构建:更精确
局限:
- 启动时间:JVM 启动慢
- 配置复杂:大型项目难维护
- 远程缓存弱:缺少分布式构建
第四代:2010s-2020s - Bazel, Buck, Gradle Enterprise
Bazel(Google)
# BUILD.bazel(2015)
py_binary(
name = "main",
srcs = ["main.py"],
deps = [":utils"],
)
py_library(
name = "utils",
srcs = ["utils.py"],
deps = ["@numpy//:numpy"],
)
# WORKSPACE(2015)
load("@bazel_tools//tools/builddefs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_python",
urls = ["https://github.com/bazelbuild/rules_python/archive/..."],
)
特点:
- 精确依赖:输入哈希,可复现构建
- 分布式缓存:远程执行和缓存
- 多语言:Java、C++、Python、Go 等
- 大规模:Google 内部使用
Buck(Facebook/Meta)
# BUCK(2013)
python_binary(
name = "main",
main_module = "main",
deps = [":utils"],
)
python_library(
name = "utils",
srcs = ["utils.py"],
deps = [
third_party_lib("numpy"),
],
)
特点:
- 快速构建:并行执行和缓存
- 多语言支持:Java、C++、Python、Objective-C
- Cell:Facebook 内部分布式执行
Gradle Enterprise
// settings.gradle
plugins {
id 'com.gradle.enterprise'
}
gradleEnterprise {
server = "https://gradle.mycompany.com"
}
特点:
- 远程缓存:公司级缓存共享
- 构建扫描:性能分析
- 依赖洞察:可视化依赖图
第五代:2020s-2025 - Buck2, Pants, Nix
Buck2(Meta)
# TARGETS(2024)
load("@prebuilt//python:defs.bzl", "python_binary")
python_binary(
name = "main",
main_module = "main",
deps = [":utils"],
)
load("//toolchains/python:toolchains/python:defs.bzl", "toolchain")
toolchain(
name = "python",
toolchain_type = "python",
)
Buck2 变化:
- Starlark 2:更好的类型系统
- 配置继承:减少重复
- 工具链抽象:统一的工具链管理
- REPL:交互式查询和构建
Pants(Toolchain)
# BUILD(2020+)
python_sources(
name = "src",
sources = ["*.py"],
deps = [
"3rdparty/python:requests",
],
)
pex_binary(
name = "app",
entry_point = "main:main",
)
特点:
- 统一接口:多种语言同一 API
- 多平台:本地执行、远程缓存、Docker
- 增量:精确到函数级别
- Type 驱动:Starlark、Python
Nix(纯函数式)
# shell.nix(2014)
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = with pkgs; [
python3
python3Packages.numpy
python3Packages.pandas
];
}
特点:
- 纯函数式:可复现,无隐式依赖
- 多环境:dev、test、prod
- Rollback:历史可追踪
现代:2025+ - Nx, Turborepo, esbuild
Nx(Monorepo 工具)
// project.json(2020+)
{
"name": "my-app",
"sourceRoot": "apps/my-app/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/vite:build"
},
"test": {
"executor": "@nx/vite:test"
}
}
}
特点:
- Monorepo 原生:跨项目依赖和缓存
- 分布式缓存:团队共享缓存
- 可视化图:依赖关系图
- 代码生成:自动 scaffold
Turborepo
// turbo.json(2021)
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
}
}
}
特点:
- 零配置:自动推断
- 增量构建:最小化执行
- CI 优化:单次运行多个目标
esbuild(JavaScript 打包)
// esbuild.config.mjs(2020)
import esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
minify: true,
});
特点:
- 极快:比 Webpack 快 10-100 倍
- 零配置:简单到极致
- Tree-shaking:移除未使用代码
对比总结
| 时代 | 代表工具 | 特点 | 局限 |
|---|---|---|---|
| 1970s | Make | 简单、依赖管理 | 不语言感知、增量弱 |
| 1990s | Ant/Maven | 语言感知、依赖管理 | XML 繁琐、全局锁 |
| 2000s | Gradle/SBT | DSL、增量构建 | 启动慢、配置复杂 |
| 2010s | Bazel/Buck | 精确依赖、分布式缓存 | 学习曲线陡 |
| 2020s | Pants/Buck2 | 类型驱动、统一接口 | 社区小 |
| 2025+ | Nx/Turborepo | Monorepo 原生、可视化 | 限于 Monorepo |
选择建议
单项目(< 1000 行)
| 语言 | 推荐 |
|---|---|
| JavaScript/TypeScript | esbuild、Vite |
| Python | Poetry + Make |
| Rust | Cargo |
| Go | go build |
中等项目(1000-10000 行)
| 语言 | 推荐 |
|---|---|
| JVM | Gradle |
| Python | Poetry |
| C/C++ | CMake + Ninja |
| Node.js | Nx(如果 Monorepo) |
大型项目(> 10000 行)
| 场景 | 推荐 |
|---|---|
| Monorepo | Nx、Turborepo |
| 多语言 | Bazel、Pants |
| 微服务 | 每个独立工具 |
| Google/Meta 风格 | Bazel、Buck2 |
趋势
1. 配置即代码
// TypeScript 配置
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
assetFileNames: 'assets/[name]-[hash].js',
},
},
},
});
2. 缓存为王
本地缓存 → 远程缓存 → 分布式执行
每层避免 50-90% 重复工作
3. 并行优先
# 自动并行
targets = [
"build:frontend",
"build:backend",
"test:unit",
"test:integration",
]
# 自动检测依赖,最大化并行
4. 增量构建
函数级别 → 文件级别 → 包级别 → 项目级别
越细粒度,越少的重构建
总结
开发者工具演进 50 年:
- 1970s:Make - 简单声明式
- 1990s:Ant/Maven - 语言感知
- 2000s:Gradle/SBT - DSL 友好
- 2010s:Bazel/Buck - 精确依赖、分布式
- 2020s:Pants/Buck2 - 类型驱动
- 2025+:Nx/Turborepo - Monorepo 原生
核心趋势:
- 从脚本到 DSL
- 从顺序到并行
- 从本地到分布式
- 从单一项目到 Monorepo
“最好的工具是你不需要思考的工具。“