Open the average web project in 2026 and before you've written a line of your own code, there's a build pipeline waiting for you: a bundler, a transpiler, a dependency tree thousands of packages deep, and a CI job whose entire purpose is to turn the code you wrote into the code that actually runs. For a lot of applications that machinery earns its keep. For a small-business site, it's frequently the single largest source of friction in the whole project — and most of the time, it's optional.
A meaningful share of what I ship has no build step at all. The HTML in the repo is the HTML the browser receives. The JavaScript I write is the JavaScript that runs. There's no compile, no bundle, no transform standing between the source and production. This post is about why I keep choosing that, what it costs, and the line where I stop.
What a build step quietly costs
The pitch for a build pipeline is real: tree-shaking, minification, modern syntax on old browsers, a tidy dist/ folder. But every one of those benefits arrives bundled with a tax that doesn't show up until later:
- A dependency tree you now own. A bundler and its plugins pull in hundreds of transitive packages. Each one is a thing that can break, go unmaintained, or ship a vulnerability you inherit.
- A gap between source and reality. When a bug only appears in the built output, you're now debugging code you didn't write. Source maps help, but you've added a translation layer between what you typed and what the user runs.
- Toolchain rot. Come back to a build-heavy project in eighteen months and the install fails — a transitive dep dropped a version, the bundler had a breaking release, the Node version moved on. The code didn't change; its scaffolding did.
- Cognitive overhead. Every contributor, including future-me, has to hold the pipeline in their head before they can change a button color with confidence.
A build step is a bet that the time it saves you in production exceeds the time it costs you in maintenance. For a brochure site or a lean web app, that bet usually loses.
What build-free actually looks like
The architecture is deliberately boring. Static HTML, CSS, and vanilla JavaScript live in the repo exactly as they're served. A Cloudflare Worker fronts everything — serving the static assets and handling the handful of dynamic endpoints a small site actually needs, like a contact form or a feedback submission. Deployment is a git push.
# the entire "build" pipeline
git push origin main
# Cloudflare's GitHub integration deploys on push.
# no CI artifact, no bundle, no dist/ folder.
That's not a simplified example. That's the real flow on a number of live projects. The code in the editor is the code in production, and the path between them has no moving parts to fail.
Modern browsers removed most of the reasons
A lot of the build-step orthodoxy is muscle memory from a harder era. The browser quietly absorbed most of what bundlers were invented to paper over:
- Native ES modules.
importandexportwork in every current browser, so you can split code into files without a bundler stitching them back together. - HTTP/2 and HTTP/3. The "bundle everything into one file to save round trips" argument was an artifact of HTTP/1. Multiplexed connections made serving several small files cheap.
- Baseline modern syntax. The transpile-to-ES5 ritual targets browsers that effectively no longer exist in real traffic. You can write current JavaScript and ship it as-is.
- Native CSS power. Custom properties, nesting, and container queries cover what a preprocessor used to be required for.
Where I stop
This isn't a crusade. A build step is the right call the moment a project genuinely needs what it provides, and I reach for one without hesitation when:
- The front end is a real application. A complex, stateful single-page app with a component framework wants a bundler. Fighting that is its own kind of waste.
- TypeScript is pulling its weight. On a larger codebase the type safety is worth the compile, and I'll happily add the step to get it.
- The asset budget is tight and measured. If shaving every kilobyte is a real requirement backed by real numbers, minification and tree-shaking earn their place.
The mistake isn't using a build step. The mistake is reaching for one reflexively, on a five-page business site, because the tutorial did — and then carrying that complexity for the entire life of the project. The right default is the simplest architecture that meets the requirement, and for a lot of real-world work, the simplest architecture has no build step in it at all.
// want it simple and fast
If you've got a site that's slow to change or expensive to maintain, the architecture is often the reason. Let's look at what it actually needs — and what it doesn't.
transmit_message