필사 모드: Build Systems & Monorepo Tools 2026 — Bazel 8 · Pants 2 · Buck2 · Nx 20 · Turborepo 2 · Moon · Lerna 8 · Rush · pnpm Workspaces Deep Dive
EnglishPrologue — why build systems got interesting again
A 2026 platform meeting at a typical company.
Junior: "We're a JS monorepo, Turborepo is enough, right?"
Senior: "Then how do we build the Go backend and the Python data team together?"
Junior: "...oh."
That tiny exchange has all of 2026 in it. On one side: **language-native tools** (cargo, gradle, npm scripts). On the other side: **polyglot** systems (Bazel, Buck2, Pants). And between them, the **task runner + cache** layer (Nx, Turborepo, Moon).
This piece is the full map as of May 2026. Bazel 8 with Bzlmod by default, Buck2 settling as OSS, Pants 2 owning Python, the Nx 20 vs Turborepo 2 split, Moon climbing the polyglot ladder, and Gradle 8.10 / Maven 4 / sbt 1.10 in JVM land. Plus what Korean and Japanese big tech actually ships with.
1. The 2026 build-system map — two axes
There's too much packed into the phrase "build system." The easiest cut is two axes.
| Axis 1 / Axis 2 | Single language | Polyglot |
| --- | --- | --- |
| Local cache only | cargo, npm, go build | GNU Make, just, Task |
| Remote cache/exec | Gradle + Develocity, Nx Cloud, Turborepo Remote Cache | Bazel, Buck2, Pants |
Vertical: **language coverage**. Horizontal: **cache/distribution depth**. The 2026 trend is that the bottom-right cell (polyglot + remote) is filling up fast. And within that cell, two camps split: the **Bazel/Buck2/Pants** camp ("declarative graph + interpreted BUILD files") and the **Nx/Turborepo/Moon** camp ("JSON/YAML config + run only affected projects").
Starting point is simple.
1. **Single language + small team** → that language's native tool (cargo, gradle, npm).
2. **Single language + large monorepo** → native tool + remote cache (Develocity, Nx Cloud).
3. **JS/TS-heavy + multiple packages** → pnpm workspaces / Nx / Turborepo.
4. **Polyglot + large scale** → Bazel / Buck2 / Pants.
2. Monorepo vs polyrepo — the 2026 answer
This debate is basically settled. The answer is "both, but monorepo is the default."
**Monorepo wins**: atomic changes (one PR touches library, service, tests at once), single dependency graph, shared infra (build, CI, lint), one-IDE search and refactor.
**Polyrepo wins**: permission isolation, build-time isolation, team autonomy, the simplicity of a small repo.
The 2026 compromise: **"several monorepos inside one company."** For example `platform-monorepo` (infra), `product-monorepo` (services), `data-monorepo` (pipelines). Each one is a monorepo, but company-wide it's polyrepo. Parts of Google, Meta, and Microsoft run this pattern. For a small company, just have one monorepo.
`Trunk-based development` + `feature flags` + `change detection` (see section 21) together erase most of the historical monorepo downsides.
3. Bazel 8 — the polyglot build standard
Bazel is the OSS version of Google's internal Blaze. Stable in May 2026 is **Bazel 8.x**, LTS is 7.x. The major shift: **Bzlmod** (MODULE.bazel) is now the default and WORKSPACE mode is deprecated.
License: Apache 2.0. Used by Google, X (formerly Twitter), Stripe, Pinterest, Spotify, Snap, Dropbox, Coupang, Mercari.
Core concepts.
- **`BUILD.bazel`**: one per directory, declares build targets in that directory.
- **`WORKSPACE` / `MODULE.bazel`**: repo root, declares external deps and toolchains.
- **Starlark**: Python subset, the language for writing build rules.
- **rules**: language-specific rule sets (`rules_go`, `rules_python`, `rules_js`, `rules_rust`, `rules_java`).
- **sandbox**: every action runs in an isolated directory, guaranteeing hermeticity.
- **remote cache / remote execution**: actions are keyed by hash, misses are dispatched to remote workers.
Simplest Go binary.
BUILD.bazel
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_library(
name = "hello_lib",
srcs = ["main.go"],
importpath = "example.com/hello",
)
go_binary(
name = "hello",
embed = [":hello_lib"],
)
bazel build //cmd/hello:hello
bazel test //...
bazel query 'deps(//cmd/hello:hello)' | head
Bzlmod dependency declaration.
MODULE.bazel
module(name = "myrepo", version = "0.1.0")
bazel_dep(name = "rules_go", version = "0.55.0")
bazel_dep(name = "gazelle", version = "0.41.0")
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//:go.mod")
use_repo(go_deps, "com_github_pkg_errors")
Bazel's strength is that **graph + cache + remote execution** are deeply integrated. The weakness is the learning curve and the chore of hand-writing BUILD files (mitigated by generators like Gazelle).
4. Buck2 (Meta) — Buck rewritten in Rust
Buck1 was Meta's Bazel cousin from 2013. In 2023 Meta rewrote it from scratch in Rust and released it OSS — **Buck2**. As of 2026 it's the standard internal build at Meta, and OSS adoption has been growing.
Compared with Bazel.
| Item | Bazel | Buck2 |
| --- | --- | --- |
| Implementation | Java | Rust |
| Config language | Starlark | Starlark |
| Action graph | Determined at rule evaluation | Dynamic Dependencies, can mutate mid-build |
| Remote execution | REAPI (BuildBuddy, EngFlow) | REAPI (BuildBuddy, EngFlow) |
| License | Apache 2.0 | Apache 2.0 |
| Main users | Google, X, Stripe | Meta, Discord |
The biggest difference is **dynamic dependencies**. Bazel separates BUILD evaluation → action graph → execution, so the graph can't change once the build starts. Buck2 lets some nodes declare additional inputs mid-build, which naturally captures things like OCaml `.ml` / `.mli` dependencies driven by compilation output.
BUCK
load("@prelude//rules.bzl", "cxx_binary", "cxx_library")
cxx_library(
name = "hello_lib",
srcs = ["hello.cc"],
headers = ["hello.h"],
)
cxx_binary(
name = "hello",
srcs = ["main.cc"],
deps = [":hello_lib"],
)
buck2 build //cpp/hello:hello
buck2 test //...
buck2 cquery 'deps(//cpp/hello:hello)'
Is migrating from Bazel worth it? Usually no. Only consider Buck2 for **a new polyglot monorepo** + **Meta-influenced org** + **genuine need for dynamic deps**.
5. Pants 2 — the de-facto Python monorepo
Pants v1, from Twitter in 2014, was a Java/Scala build system. Starting with v2, **Toolchain Labs** rebuilt it from scratch as a **Python-first polyglot**. Stable in May 2026 is **Pants 2.27**.
License: Apache 2.0. Used at Slack, IBM, Pico, Toolchain.
What sets Pants 2 apart.
- **Python-first**: first-class support for `pytest`, `mypy`, `ruff`, `pyright`, `black`, `flake8`.
- **Automatic dependency inference**: import graph analysis adds deps to BUILD files automatically.
- **Lockfile-aware**: integrates PEP 621 / pyproject.toml with lockfiles.
- **Remote caching**: compatible with BuildBuddy and EngFlow.
pants.toml
[GLOBAL]
pants_version = "2.27.0"
backend_packages = [
"pants.backend.python",
"pants.backend.python.lint.ruff",
"pants.backend.python.typecheck.mypy",
"pants.backend.docker",
]
[python]
interpreter_constraints = ["==3.12.*"]
src/python/myapp/BUILD
python_sources(name="lib")
python_tests(name="tests", dependencies=[":lib"])
pex_binary(name="bin", entry_point="myapp.main:main")
pants tailor :: # auto-generate BUILD files
pants lint test check :: # lint/test/typecheck everything
pants --changed-since=main test # only what changed
pants package src/python/myapp:bin
For a Python monorepo Pants 2 is nearly the default. Caching mypy/ruff results is what seals the deal.
6. Remote execution backends — BuildBuddy / EngFlow / NativeLink
Bazel, Buck2, and Pants all speak the **Remote Execution API** (REAPI), a gRPC standard. Cache (action result, CAS) and execution (workers) are decoupled: on a cache miss the client delegates execution to a remote worker.
Main 2026 backends.
| Product | Company | Notes |
| --- | --- | --- |
| BuildBuddy | BuildBuddy Inc | Open-core, great UI, GCP/AWS SaaS + self-host |
| EngFlow | EngFlow | Enterprise focus, used by Spotify, Tesla |
| NativeLink | TraceMachina | OSS, Rust, still maturing but fast |
| Buildbarn | OSS | Fully open source, run it yourself |
BuildBuddy config example (`.bazelrc`).
build --bes_results_url=https://app.buildbuddy.io/invocation/
build --bes_backend=grpcs://remote.buildbuddy.io
build --remote_cache=grpcs://remote.buildbuddy.io
build --remote_executor=grpcs://remote.buildbuddy.io
build --remote_header=x-buildbuddy-api-key=YOUR_API_KEY
That single block turns on cache + execution + Build Event Stream (BES, web UI for tracing builds). On a big monorepo, build times often drop 5–10x.
7. Nx 20 — the JS/TS monorepo heavyweight
Nx is the JS/TS monorepo tool from Nrwl (now Nx). Stable in May 2026 is Nx 20.x. License MIT. The hosted offering is Nx Cloud (paid SaaS).
Two core wins.
1. **Local + remote cache**: hash-keyed cache of task outputs (build, test, lint).
2. **affected**: run only the projects impacted by a git diff.
npx create-nx-workspace@latest myorg --preset=ts
cd myorg
nx g @nx/react:app web
nx g @nx/node:app api
nx g @nx/js:lib shared
// nx.json
{
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud",
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"],
"accessToken": "YOUR_NX_CLOUD_TOKEN"
}
}
},
"namedInputs": {
"default": ["{projectRoot}/**/*"],
"production": ["default", "!{projectRoot}/**/*.spec.ts"]
}
}
nx run-many -t build # build everything
nx affected -t test # test only affected projects
nx graph # visualize project dep graph
nx release # version, changelog, publish
New in Nx 20: **Atomizer** (test splitting), **Custom Conformance Rules** (monorepo rule checks), **Self-Healing CI** (auto-retry + analyze failed e2e). After the Lerna acquisition the integration is essentially done — Lerna is now closer to an alias for Nx.
8. Turborepo 2 — Vercel's JS monorepo tool
Turborepo was built by Jared Palmer in 2021. Acquired by Vercel in 2022. **Turborepo 2.0** (2024) rewrote the core in Rust, and 2.x is the May 2026 stable.
Versus Nx.
| Item | Nx | Turborepo |
| --- | --- | --- |
| Philosophy | "Smart" workspace (generators, plugins, graph) | "Simple" task runner + cache |
| Config | nx.json + project.json | turbo.json |
| Code gen | Rich generators | Almost none |
| Cache | Local + Nx Cloud | Local + Remote Cache (Vercel free tier, self-hostable) |
| Distributed exec | Nx Agents | None (use CI shards) |
| Language | JS/TS focus, some polyglot | JS/TS only |
| Learning curve | Medium-high | Low |
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"],
"inputs": ["src/**", "package.json", "tsconfig.json"]
},
"test": { "dependsOn": ["^build"], "outputs": ["coverage/**"] },
"lint": { "outputs": [] },
"dev": { "cache": false, "persistent": true }
}
}
turbo run build # full build (cache hits skipped)
turbo run test --filter=...[origin/main] # changed only
turbo run dev --parallel # parallel dev servers
turbo prune --scope=web # slice down for Docker build
Turborepo's killer combo is **simplicity + Vercel integration**. Set `TURBO_TOKEN` + `TURBO_TEAM` in CI and Vercel Remote Cache turns on automatically. For Next.js / Remix-shaped teams, Turborepo is the lowest-friction path.
9. Moon (moonrepo) — Rust-built polyglot dark horse
Moon appeared in 2022 as a Rust-based build system. Built by Miles Johnson, formerly of Lerna and Yarn. License MIT. Goal: unify JS/TS, Python, Rust, Go, Deno, and Bun under one tool.
Difference from Nx/Turborepo.
- **Language-agnostic**: `lang.toml` pins toolchain (Node, Deno, Bun, Rust, Python) versions.
- **Project graph**: directory-scoped like Bazel, but configured in YAML.
- **Affected**: same concept as Nx.
- **Mise integration**: pairs with the Mise version manager to bootstrap toolchains.
.moon/workspace.yml
projects:
- 'apps/*'
- 'packages/*'
vcs:
manager: 'git'
defaultBranch: 'main'
runner:
cacheLifetime: '7 days'
archivableTargets: ['build', 'test']
apps/web/moon.yml
language: 'typescript'
type: 'application'
dependsOn:
- 'shared'
tasks:
build:
command: 'next build'
outputs: ['.next']
test:
command: 'vitest run'
moon run web:build
moon ci # every affected task
moon dep-graph
Moon is still small, but **Rust speed + polyglot + YAML config** is an attractive combo. Worth a look for teams who find Bazel heavy and Turborepo too JS-only.
10. Lerna 8 · Rush · pnpm workspaces — three lightweight JS options
There are still three lightweight JS monorepo options.
**Lerna 8**: acquired by Nrwl in 2022, now effectively part of Nx. For a new project just use Nx directly, but existing Lerna repos can opt into Nx's cache and affected logic.
// lerna.json
{ "version": "independent", "npmClient": "pnpm", "useNx": true }
lerna run build --since=origin/main
lerna publish
**Rush (Microsoft Rush Stack)**: built on top of pnpm with strict policies (no phantom deps, change-file checks). Used inside Microsoft, Office Online, parts of Azure. Pick it when a big JS monorepo needs strong guardrails.
rush install
rush build
rush change # author a change file before PR
rush publish
**pnpm workspaces (9.x)**: the simplest. `pnpm-workspace.yaml` + `package.json` `workspaces` field. No cache or affected, but great for small monorepos (under 20 packages).
pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
pnpm install
pnpm -r build # build every workspace
pnpm --filter=@org/web build # one package
pnpm --filter='...[origin/main]' test # affected only (pnpm 9+)
Quick guide: **small team + JS only + want simple** → pnpm workspaces. **Mid–large + Vercel-friendly** → Turborepo. **Mid–large + rich tooling/policies** → Nx. **Large + strict policies + MS-friendly** → Rush.
11. JVM camp — Gradle 8.10 + Maven 4
The JVM has its own deep build-tool ecosystem. Stable versions in May 2026.
**Gradle 8.10+**: Groovy/Kotlin DSL, dependency management, multi-project. Two core cache features.
- **Build cache**: caches task outputs (local + remote).
- **Configuration cache**: caches the configuration phase itself (saves seconds-to-tens-of-seconds on big projects).
- **Develocity** (formerly Gradle Enterprise): remote cache + build scan + test insights SaaS.
// build.gradle.kts
plugins {
java
application
id("org.springframework.boot") version "3.4.1"
}
group = "com.example"
version = "0.1.0"
repositories { mavenCentral() }
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.test { useJUnitPlatform() }
./gradlew build --build-cache --configuration-cache
./gradlew :api:test
./gradlew dependencies
**Maven 4** (GA in 2025): keeps pom.xml stable while strengthening the build graph, parallelism, and the daemon (`mvnd`). Still the first choice thanks to Spring's gravitational pull. `mvnd` (Maven Daemon) avoids JVM warmup, getting close to Gradle in perceived speed.
./mvnw -T 1C clean install # 1 thread per CPU core in parallel
mvnd -T 1C clean install # daemon mode
For a JVM monorepo: new projects pick Gradle + Develocity, legacy/SI projects stay with Maven 4 + mvnd.
12. sbt 1.10 · Mill — Scala camp
There's a separate Scala post, but briefly from a build perspective.
**sbt 1.10**: the Scala default, incremental compile is its strength. The eternal complaint is the steep learning curve.
// build.sbt
ThisBuild / scalaVersion := "3.6.0"
lazy val root = (project in file("."))
.settings(
name := "myapp",
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect" % "3.5.4",
"org.scalatest" %% "scalatest" % "3.2.19" % Test,
)
)
**Mill (Lihaoyi)**: a Scala/Java/JS build expressed in Scala. Cleaner and faster than sbt, in the consensus view. Not mainstream, but new Scala teams sometimes pick it.
// build.mill
package build
object app extends ScalaModule {
def scalaVersion = "3.6.0"
def ivyDeps = Agg(ivy"org.typelevel::cats-effect:3.5.4")
}
A genuinely large Scala monorepo basically only has Bazel + `rules_scala` as the answer, but few teams ever reach that size.
13. C/C++ — CMake 3.31 + Ninja 1.12 / Meson / Bazel
C/C++ had the longest build-system warring states. The 2026 summary.
**CMake 3.31 + Ninja 1.12**: the de-facto standard. CMake isn't a build system — it's a build-system **generator** (produces Makefiles, Ninja, MSBuild). Ninja runs the result fastest.
CMakeLists.txt
cmake_minimum_required(VERSION 3.31)
project(myapp CXX)
set(CMAKE_CXX_STANDARD 23)
add_library(mylib STATIC src/foo.cc src/bar.cc)
target_include_directories(mylib PUBLIC include)
add_executable(myapp src/main.cc)
target_link_libraries(myapp PRIVATE mylib)
cmake -S . -B build -G Ninja
cmake --build build -j
ctest --test-dir build
**Meson + Ninja**: CMake's cousin, cleaner syntax (Python-ish). Used by GNOME, systemd.
**Bazel + rules_cc**: the answer for large polyglot C/C++ monorepos. The C++ services at Google, Stripe, and Pinterest live on this path.
**GNU Make**: still the first pick for small projects. Lowest learning curve.
14. Rust — Cargo workspaces + cargo-nextest
A Rust monorepo is almost always fine with **Cargo workspaces**.
Cargo.toml (root)
[workspace]
resolver = "2"
members = ["crates/*", "services/*"]
[workspace.dependencies]
tokio = { version = "1.42", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
cargo build --workspace
cargo test --workspace
cargo nextest run --workspace # faster test runner
cargo metadata --format-version 1 # dep graph
If you need a remote cache, `sccache` (Mozilla) is standard. Backends: S3, GCS, Redis, memcached, local disk.
export RUSTC_WRAPPER=sccache
export SCCACHE_BUCKET=my-sccache-bucket
cargo build --release
sccache --show-stats
When Rust joins a large polyglot stack, Bazel + `rules_rust` is on the table, but the onboarding cost is real. Without a company-wide Bazel standard, sccache + Cargo is almost always the answer.
15. Go — go modules + gomonorepo patterns
Go's module system is simple, which makes monorepos natural. One `go.mod` at the repo root, with `internal/` for package boundaries.
myrepo/
go.mod
cmd/
api/main.go
worker/main.go
internal/
auth/
storage/
pkg/
publicapi/
go build ./...
go test ./...
go vet ./...
go test -count=1 -run TestFoo ./internal/auth
When caching and remote execution matter, Bazel + `rules_go` + `gazelle` is the most mature combo. Gazelle generates `BUILD.bazel` from `go.mod`.
bazel run //:gazelle
bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=deps.bzl%go_dependencies
The Go services at Pinterest, Twitter, and Coupang live on this path.
16. Mise (formerly rtx) · just · Task — lightweight task runners
Separate from build systems, there's the **"call repeated commands by short names"** family. Successors to shell scripts and Makefiles.
**Mise** (formerly rtx): asdf-compatible version manager + task runner. Written in Rust. One tool pins Node, Python, Go, Rust, Ruby, and Bun versions, and defines tasks.
mise.toml
[tools]
node = "22"
python = "3.12"
go = "1.23"
[tasks.build]
description = "Build all"
run = "pnpm -r build"
[tasks.test]
description = "Run tests"
depends = ["build"]
run = "pnpm -r test"
mise install # install the toolchain
mise run build
mise run test
**just**: a modern Make. Clean syntax, no dependencies.
justfile
default:
@just --list
build:
pnpm -r build
test: build
pnpm -r test
release version:
git tag v{{version}}
git push origin v{{version}}
**Task** (taskfile.dev): YAML-based task runner, written in Go.
Taskfile.yml
version: '3'
tasks:
build:
cmds:
- pnpm -r build
test:
deps: [build]
cmds:
- pnpm -r test
These three are **not graph-aware build tools** — they're command shorthands. Great as the entry point even on large monorepos: `just ci` can call `bazel test //...` or `nx affected -t test` under the hood.
17. Remote cache — Bazel BES vs Nx Cloud vs Turborepo Remote Cache
The three tools cache remotely in different ways.
| System | Cache key | Storage | Backend |
| --- | --- | --- | --- |
| Bazel | action hash (inputs + command + env) | CAS (Content Addressable Storage) | BuildBuddy, EngFlow, Buildbarn |
| Nx Cloud | task hash (inputs + command + env) | Nx Cloud SaaS or self-host | Nx Cloud (proprietary) |
| Turborepo Remote Cache | task hash | Vercel SaaS or self-host | Vercel, Turborepo Server (OSS) |
**Bazel strengths**: REAPI standard, multi-backend, cache hits are reliable thanks to sandboxing.
**Nx Cloud strengths**: web UI, failed-build insights, Nx Agents (distributed execution) integration.
**Turborepo strengths**: easiest setup (two env vars), automatic Vercel CI integration.
All three offer self-hosting, but ops effort scales BuildBuddy/Buildbarn >> Nx Cloud (Helm chart) >> Turborepo Server (one Docker container).
18. Hermetic builds + content-addressed cache — the essence of reproducibility
"It worked yesterday, broken today" almost always comes from **hidden inputs** (env vars, system libraries, the clock, the network). The fix is hermetic builds + content-addressed caching.
**Hermetic**: explicitly declare an action's inputs (sources + tools + env vars) and block external access (sandbox). Bazel, Buck2, and Pants do this by default.
**Content-addressed**: assume that identical input hashes produce identical outputs. Any byte of input change forces a rebuild; identical inputs hit the cache.
input_hash = sha256(sources + tools + env + command)
output: stored under output_hash
cache_key: input_hash -> output_hash
Nix follows the same philosophy (different tool from Bazel, same idea). Reproducible CI/CD becomes actually achievable.
19. Container build — BuildKit / Buildah / Kaniko / Earthly
The build-systems story ends with containers because in 2026 every service ships as an OCI image, and **build-system output → container image** is the final step of the pipeline.
| Tool | Daemon needed | Notes |
| --- | --- | --- |
| Docker BuildKit | Yes (dockerd) | Standard, cache mounts, SBOM, provenance |
| Buildah / Podman build | No | Daemonless, Red Hat |
| Kaniko | No | Build containers inside a container, K8s CI-friendly |
| Earthly | Uses daemon | Dockerfile + Makefile hybrid, strong caching |
| ko (Go only) | No | Pack Go binaries into distroless images instantly |
| Jib (JVM only) | No | Maven/Gradle plugin, image build without Docker |
Bazel uses `rules_oci` (successor to `rules_docker`) to build OCI images hermetically. Image layers stay byte-identical across rebuilds when inputs match.
BUILD.bazel
load("@rules_oci//oci:defs.bzl", "oci_image")
oci_image(
name = "api_image",
base = "@distroless_base",
entrypoint = ["/api"],
tars = [":api_layer"],
)
20. Build systems at global big tech
| Company | Primary build |
| --- | --- |
| Google | Blaze (internal Bazel) |
| Meta | Buck2 |
| Microsoft | Rush + internal systems |
| Amazon | Brazil (internal) + Cargo / Maven |
| X (formerly Twitter) | Bazel (migrated from Pants) |
| Pinterest | Bazel (Python/Java/Go) |
| Stripe | Bazel |
| Spotify | Bazel + EngFlow |
| Snap | Bazel |
| Dropbox | Bazel (some Python on Pants) |
| Slack | Pants (Python, TS) |
| Vercel | Turborepo |
| Shopify | Bazel (services), nx/turborepo (storefront) |
| GitHub | Internal + Bazel |
| Discord | Buck2 |
Polyglot + large scale converges on Bazel/Buck2/Pants.
21. Korean and Japanese examples
**Korea**
- **Coupang**: heavy Bazel adoption. Search, logistics, parts of iOS/Android. The C++ search infra runs on Bazel hermeticity.
- **Toss / Viva Republica**: frontend on Turborepo. Backend is Gradle-heavy.
- **Naver**: broad usage. Some Bazel in NHN/Naver Pay; many frontend teams on Turborepo or Nx.
- **Kakao**: Gradle/Maven-centric, frontend on Turborepo / pnpm workspaces.
- **Woowa Brothers (Baemin)**: Gradle + Spring-centric. Frontend on Nx/Turborepo.
- **Karrot (당근)**: pnpm workspaces + Turborepo is the standard. Some Nx adoption.
**Japan**
- **Mercari**: heavy Bazel. Go/Java microservices on Bazel + BuildBuddy.
- **LINE Yahoo**: Bazel (LY) and Gradle (LINE legacy). Search/ads infra on Bazel.
- **CyberAgent / Ameba**: Nx + Turborepo. Bazel on parts of the ad platform.
- **Recruit / Indeed**: heavy Bazel. Indeed has published frequently on monorepo + Bazel.
- **DeNA**: Gradle-centric with some Bazel.
- **Rakuten**: Gradle + Maven, some Bazel.
Worth noting: Japanese big tech adopts Bazel at a higher rate than Korea, with Mercari, Indeed, and LY leading.
22. When to pick what — the decision tree
Putting it all together.
1. **Small JS team (~5 people, ~10 packages)** → `pnpm workspaces` alone.
2. **Mid-size JS/TS team (~30 people, ~50 packages) + Vercel/Next.js-heavy** → `Turborepo 2` + Vercel Remote Cache.
3. **Mid-size JS/TS team + need rich tooling/policies** → `Nx 20` + Nx Cloud.
4. **JS + Python + Go polyglot monorepo** → `Moon` or `Bazel`.
5. **Python monorepo (data/ML team)** → `Pants 2`.
6. **New JVM project** → `Gradle 8.10` + Develocity.
7. **Legacy/SI JVM project** → `Maven 4` + `mvnd`.
8. **Rust monorepo** → `Cargo workspaces` + `sccache` + `cargo-nextest`.
9. **Go monorepo** → single `go.mod`, add Bazel + Gazelle when needed.
10. **C/C++** → `CMake + Ninja` (small) or `Bazel` (large).
11. **Scala** → `sbt` or `Mill`, only `Bazel + rules_scala` if truly huge.
12. **Massive polyglot + remote execution required** → `Bazel` + BuildBuddy/EngFlow, or `Buck2`.
The most common mistake is "adopt Bazel because it's trending, regret it six months later." Only consider a polyglot build system when **build time is genuinely a bottleneck**, **the bottleneck is solvable by remote cache/execution**, and **you have maintainers**. Otherwise, that language's native tool is almost always correct.
References
- Bazel docs — https://bazel.build/
- Bazel Bzlmod (MODULE.bazel) — https://bazel.build/external/module
- Buck2 — https://buck2.build/
- Buck2 GitHub — https://github.com/facebook/buck2
- Pants 2 docs — https://www.pantsbuild.org/
- Nx docs — https://nx.dev/
- Nx Cloud — https://nx.app/
- Turborepo — https://turbo.build/repo
- Turborepo 2 launch — https://vercel.com/blog/turborepo-2-0
- Moon (moonrepo) — https://moonrepo.dev/
- Lerna — https://lerna.js.org/
- Rush Stack — https://rushjs.io/
- pnpm Workspaces — https://pnpm.io/workspaces
- Yarn Workspaces — https://yarnpkg.com/features/workspaces
- Gradle — https://gradle.org/
- Develocity (formerly Gradle Enterprise) — https://gradle.com/develocity/
- Apache Maven — https://maven.apache.org/
- mvnd (Maven Daemon) — https://github.com/apache/maven-mvnd
- sbt — https://www.scala-sbt.org/
- Mill — https://mill-build.org/
- CMake — https://cmake.org/
- Ninja — https://ninja-build.org/
- Meson — https://mesonbuild.com/
- BuildBuddy — https://www.buildbuddy.io/
- EngFlow — https://www.engflow.com/
- NativeLink — https://www.nativelink.com/
- Buildbarn — https://github.com/buildbarn
- Mise (formerly rtx) — https://mise.jdx.dev/
- just — https://github.com/casey/just
- Task (taskfile.dev) — https://taskfile.dev/
- Cargo Workspaces — https://doc.rust-lang.org/cargo/reference/workspaces.html
- cargo-nextest — https://nexte.st/
- sccache — https://github.com/mozilla/sccache
- Remote Execution API (REAPI) — https://github.com/bazelbuild/remote-apis
- BuildKit — https://github.com/moby/buildkit
- rules_oci — https://github.com/bazel-contrib/rules_oci
- Mercari Engineering / Bazel — https://engineering.mercari.com/en/blog/
Epilogue — "Builds are graphs, always"
If you compress 30 years of build systems into one line, it's **"a directed graph from inputs to outputs — reuse the nodes whenever you can."** What Make tried to do, Bazel does with sandboxing + remote execution, Turborepo does with a JS-only simplification, and Pants does in a Python-friendly way.
The tools rotate, but the principle is constant: **"know the exact inputs to your build, cache the outputs, never rerun on identical inputs."** 2026's polyglot build systems apply that principle **across multiple languages and machines**. Small teams have it covered by tools that implement the principle closest to home (pnpm + Turborepo, Cargo, Gradle); huge teams may not be able to avoid Bazel.
One last note — a build system's success depends on **"who maintains the BUILD files,"** not the tool itself. People, not tooling. Bazel or Nx, without a **dedicated build-infra owner (one or two engineers)**, success is roughly halved. Without the headcount budget, the right build system for that company is usually a simpler one.
현재 단락 (1/483)
A 2026 platform meeting at a typical company.