Build Tools
Compilation, bundling, task automation, and build systems for modern development.
Build Tool Categories
| Category | Purpose | Examples |
|---|---|---|
| Language-native | Compile, test, package for specific language | cargo, go build, gradle, mvn |
| Polyglot/Task runners | Orchestrate multi-language builds | make, just, task, earthly |
| JS/TS Bundlers | Bundle, optimize, transform frontend code | Vite, esbuild, Turbopack, Rspack |
| Container/Cloud | Build images, deploy | Docker, Buildpacks, Kaniko, BuildKit |
| Monorepo | Manage multiple packages in one repo | Turborepo, Nx, Bazel, Moonrepo |
Language-Native Build Tools
Rust: Cargo
# Cargo.toml
[package]
name = "my-service"
version = "0.1.0"
edition = "2021"
[workspace]
members = ["domain", "application", "infrastructure", "api"]
[profile.release]
lto = true
codegen-units = 1
strip = true
panic = "abort"
[features]
default = ["postgres"]
postgres = ["sqlx/postgres"]
sqlite = ["sqlx/sqlite"]
Commands: cargo build, cargo test, cargo check, cargo clippy, cargo fmt, cargo audit
Go: Go Build
# Build
go build -o bin/api ./cmd/api
# Build with version info
go build -ldflags="-X main.version=1.2.3 -X main.commit=$(git rev-parse HEAD)" -o bin/api ./cmd/api
# Cross-compile
GOOS=linux GOARCH=amd64 go build -o bin/api-linux ./cmd/api
Commands: go build, go test, go vet, go fmt, go mod tidy, go generate
JVM: Gradle (Kotlin DSL) — Recommended
// build.gradle.kts
plugins {
id("org.springframework.boot") version "3.2.0"
id("io.spring.dependency-management") version "1.1.4"
kotlin("jvm") version "1.9.0"
kotlin("plugin.spring") version "1.9.0"
kotlin("plugin.serialization") version "1.9.0"
id("com.github.johnrengelman.shadow") version "8.1.1" // Fat JAR
}
group = "com.example"
version = "1.0.0"
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
dependencies {
implementation(platform("org.springframework.boot:spring-boot-dependencies:3.2.0"))
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.kotest:kotest-runner-junit5:5.8.0")
}
tasks.withType<Test> {
useJUnitPlatform()
testLogging { events("passed", "skipped", "failed", "standardOut", "standardError") }
}
tasks.withType<ShadowJar> {
archiveClassifier.set("")
manifest {
attributes["Main-Class"] = "com.example.ApplicationKt"
}
}
Commands: ./gradlew build, ./gradlew test, ./gradlew bootRun, ./gradlew shadowJar
Python: uv / Hatch
# pyproject.toml (uv/hatch)
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "my-service"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.109",
"pydantic>=2.6",
"sqlalchemy>=2.0",
]
[project.optional-dependencies]
dev = ["pytest>=8.0", "ruff>=0.3", "mypy>=1.8"]
[tool.hatch.envs.dev]
dependencies = ["dev"]
[tool.hatch.scripts]
dev = "uvicorn main:app --reload"
test = "pytest"
lint = "ruff check . && mypy src"
format = "ruff format ."
Commands: uv sync, uv run pytest, uv run ruff check, hatch run test
JavaScript/TypeScript Bundlers
| Tool | Best For | Key Features |
|---|---|---|
| Vite | Apps (React, Vue, Svelte, Solid) | Dev server, HMR, optimized prod build |
| esbuild | Libraries, ultra-fast builds | Go-based, 10-100x faster |
| Turbopack | Next.js 14+, Turborepo | Rust-based, incremental |
| Rspack | Webpack-compatible, fast | Rust-based, webpack API |
| Rollup | Libraries, ESM | Tree-shaking, flexible plugins |
| tsup | TS libraries | esbuild-based, d.ts generation |
Vite Config (Modern Default)
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
plugins: [react(), tsconfigPaths()],
build: {
target: 'es2022',
minify: 'esbuild',
cssCodeSplit: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom', 'react-router-dom'],
},
},
},
},
server: {
port: 3000,
proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } },
},
});
Library Build (tsup)
// package.json
{
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts --external react",
"dev": "tsup src/index.ts --format esm,cjs --dts --watch"
},
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
}
}
}
Task Runners / Polyglot Orchestration
Just (Modern Make)
# justfile
set shell := ["bash", "-cu"]
default: help
help:
@just --list
build: build-rust build-go build-frontend
build-rust:
cargo build --release --workspace
build-go:
CGO_ENABLED=0 go build -o bin/api ./cmd/api
build-frontend:
cd frontend && pnpm build
test: test-rust test-go test-frontend
test-rust:
cargo test --workspace
test-go:
go test ./...
test-frontend:
cd frontend && pnpm test
lint: lint-rust lint-go lint-frontend
lint-rust:
cargo clippy --workspace -- -D warnings
cargo fmt --check --workspace
lint-go:
golangci-lint run ./...
lint-frontend:
cd frontend && pnpm lint
dev:
just build && docker compose up -d
clean:
cargo clean
go clean
rm -rf bin/ frontend/dist/
Task (Go-based, YAML)
# Taskfile.yml
version: '3'
vars:
GREETING: "Hello, {{.USER}}!"
tasks:
default:
cmds:
- task --list
silent: true
build:
deps: [build-rust, build-go, build-frontend]
build-rust:
cmd: cargo build --release --workspace
build-go:
cmd: CGO_ENABLED=0 go build -o bin/api ./cmd/api
build-frontend:
dir: frontend
cmd: pnpm build
test:
deps: [test-rust, test-go, test-frontend]
lint:
deps: [lint-rust, lint-go, lint-frontend]
Earthly (Containerized, Reproducible)
# Earthfile
VERSION 0.8
FROM rust:1.78 AS rust-build
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release
FROM golang:1.22 AS go-build
WORKDIR /app
COPY go.mod go.sum ./
COPY cmd ./cmd
COPY internal ./internal
RUN CGO_ENABLED=0 go build -o /build/api ./cmd/api
FROM node:20 AS frontend-build
WORKDIR /app
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY frontend/ ./
RUN pnpm build
build:
BUILD +rust-build+go-build+frontend-build
SAVE ARTIFACT /app/target/release/my-service AS LOCAL rust-binary
SAVE ARTIFACT /build/api AS LOCAL go-binary
SAVE ARTIFACT /app/dist AS LOCAL frontend-dist
Monorepo Tools
| Tool | Language | Best For |
|---|---|---|
| Turborepo | JS/TS/Go/Rust | Remote caching, task pipeline |
| Nx | JS/TS/Java/Go | Affected computation, generators |
| Bazel | Polyglot | Hermetic, remote execution |
| Moonrepo | Rust/JS/Go | Rust-first, fast |
| Pants | Python/Go/Java/Scala | Fine-grained, remote caching |
Turborepo (Most Popular for JS/TS)
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", "build/**", "target/release/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
}
},
"globalEnv": ["DATABASE_URL", "REDIS_URL"]
}
// package.json (workspace root)
{
"private": true,
"workspaces": [
"packages/*",
"services/*",
"tools/*"
],
"scripts": {
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint",
"dev": "turbo run dev",
"format": "prettier --write \"**/*.{ts,tsx,json,md}\""
},
"devDependencies": {
"turbo": "latest",
"prettier": "latest"
}
}
Container Build
Dockerfile (Multi-stage, Optimized)
# Build stage
FROM rust:1.78 AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y pkg-config libssl-dev
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release
# Runtime stage
FROM gcr.io/distroless/cc-debian12
COPY --from=builder /app/target/release/my-service /my-service
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/my-service"]
BuildKit Features (Docker 23+)
# syntax = docker/dockerfile:1.7
# Cache mounts for dependencies
RUN --mount=type=cache,target=/root/.cargo/registry \
--mount=type=cache,target=/app/target \
cargo build --release
# Secret mounts (build-time only)
RUN --mount=type=secret,id=npm_token \
npm ci --registry=https://npm.pkg.github.com
Buildx (Multi-platform)
docker buildx create --use --name mybuilder
docker buildx build --platform linux/amd64,linux/arm64 -t myorg/my-service:1.0.0 --push .
CI/CD Integration
GitHub Actions (Reusable Workflows)
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
uses: ./.github/workflows/reusable-build.yml
secrets: inherit
test:
needs: build
uses: ./.github/workflows/reusable-test.yml
secrets: inherit
lint:
uses: ./.github/workflows/reusable-lint.yml
Caching Strategy
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Cache Go
uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
- name: Cache Node
uses: actions/cache@v4
with:
path: |
~/.npm
~/.cache/pnpm
key: ${{ runner.os }}-node-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
Build Performance Tips
| Technique | Impact |
|---|---|
| Incremental builds | Only rebuild changed modules (Turborepo, Nx, Bazel) |
| Remote caching | Share cache across CI/local (Turbo, Nx, Bazel, Gradle Enterprise) |
| Parallel execution | cargo test --jobs 4, go test -p 4, gradle --parallel |
| Dependency caching | Cache ~/.cargo, ~/go/pkg/mod, ~/.npm, ~/.gradle |
| Shallow clones | git clone --depth=1 in CI (if history not needed) |
| Layer caching | Order Dockerfile: deps → build → copy source |
| BuildKit | Parallel stages, cache mounts, secret mounts |
Related Notes
- Developer Tools — IDE, debugging, profiling
- Static Analysis — Linting, type checking
- Package Management — Dependencies, registries
-
- — Language-specific build tools
- CI CD Best Practices — Pipeline integration
- Infrastructure as Code — Terraform, Pulumi builds
Sources: Cargo, Go, Gradle, Vite, esbuild, Turborepo, Nx, Bazel, Earthly, Docker BuildKit, GitHub Actions, CNCF CI/CD Landscape