The Build Pipeline
From source to deployable artifact: transforms, hashing, source maps, and what 'build failed' should tell you.
What a build does
Source code โ deployable artifact, via steps you configure:
- Compile/transform โ TypeScript โ JS, JSX โ JS, SCSS โ CSS
- Bundle โ many files โ few, with an import graph
- Minify โ strip comments/whitespace, shorten names (terser/esbuild)
- Hash & fingerprint โ
app.a83f2c.jsso caches can live forever (module 8) - Emit source maps โ
.mapfiles translating minified back to original for debugging prod errors
Modern tools (Vite, Next) fuse these with heavy caching โ a rebuild only redoes what changed.
Source maps: your prod debugger
Minified stack traces are unusable (a.b is not a function at 1:88231). Source maps restore names and lines. Upload them to your error tracker (Sentry et al.) โ don't serve them publicly unless you want your source readable by everyone.
The artifact is immutable
Build once, deploy everywhere. The same hashed artifact goes to staging and production โ "works in staging, differs in prod" then means environment config, not build drift. Rebuild-per-server is the anti-pattern: two builds are never identical.
What "build failed" must tell you
CI output is read at 6pm by someone who wants to go home:
- Which step failed (typecheck? tests? bundle?) โ one glance
- The actual error with file and line โ pasted above, not buried in 4,000 lines
- Whether it's yours โ PR-scoped runs prevent "main is broken, not me" confusion
Fast feedback beats thorough feedback: typecheck + unit tests in ~2 minutes on every push; the long E2E suite on PRs and main.
Environment parity
Same artifact, different config โ that's the contract. If staging lacks a service prod has, you will discover the difference in production. Keep environments structurally identical; let config variables be the only delta (and document every one).