{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreievjrhnqly4oytdvgdgnigna3dwcllq3qlw4utwwawy5m5voeogaa",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpyva2qobgq2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreieqbl2xip77ko5wanv66umvuxe2a2xir664tlclsndc2ccu3dsuh4"
},
"mimeType": "image/webp",
"size": 83006
},
"path": "/smeldr/no-pipeline-no-oil-spills-23oh",
"publishedAt": "2026-07-06T19:32:54.000Z",
"site": "https://dev.to",
"tags": [
"devops",
"webdev",
"go",
"programming",
"Smeldr"
],
"textContent": "At some point in the last decade, shipping a web application started requiring a\nbuild step. Then a second one. Then a config file for the bundler, a config file for\nthe CSS processor, a config file for the linter that runs before the bundler, and a\nCI definition that orchestrates all of them in the right order.\n\nEvery one of those steps is a dependency. Every dependency has a version. Every version\nwill, eventually, break something.\n\nWhen I started building Smeldr, I made a deliberate choice: no build pipeline. Not\n\"we will add it later.\" Not \"we use a lightweight one.\" None. The build tool is\n`go build`. The output is a single binary. That is the whole pipeline.\n\n## KISS, taken seriously\n\nKeep It Simple is easy to say. It is harder to actually do when the ecosystem keeps\noffering you tools that each solve a real problem. Webpack solves module bundling.\nPostCSS solves CSS compatibility. PurgeCSS solves stylesheet size. Each is reasonable\non its own. Together they form a chain where a breaking change in one link stops\ndeployment.\n\nSimplicity is not about being clever. It is about counting the things that can go\nwrong and removing as many as you can. A pipeline with five steps has five places to\nfail. A pipeline with zero steps has zero.\n\nThe question I kept asking was: does this tool solve a problem I actually have, or a\nproblem the tool introduced by existing?\n\n## No vendor lock-in\n\nBuild pipelines accumulate platform-specific config. A Vercel deployment looks\ndifferent from a Fly.io deployment. A Netlify function is not a plain HTTP handler.\nBefore long, the pipeline is not just a build tool, it is an implicit contract with\na specific platform.\n\nA Go binary does not care where it runs. Linux on a VPS, a container on any\norchestrator, a bare metal server in a data center. No platform-specific config.\nNo adapter layer. The deployment target is: somewhere that can run a process.\n\nThat is a decision you can change later without rewriting anything.\n\n## Deployment risk\n\nThe riskiest moment in any software project is deployment. Something that worked\nlocally is moving into production. The fewer differences between those two environments,\nthe lower the risk.\n\nA build pipeline is a set of differences. The bundler version on your laptop versus\nCI versus the version from six months ago when the last deployment happened. The\nenvironment variables that PostCSS needs to find its config. The Node version that\nthe build tooling requires, quietly different from the one in production.\n\nWith `go build`, the binary that runs on your laptop is the binary that runs in\nproduction. There is no transformation step that can introduce a discrepancy. You\ntest the artifact, you ship the artifact.\n\n## A deferred cost\n\nA build pipeline is often justified as a time saver. Configure the bundler once,\nand development goes faster.\n\nThat framing is mostly correct. The bundler does save time on the specific problems\nit was designed to solve. The time it costs shows up later, distributed across every\nupgrade cycle, every dependency audit, every CI failure that turns out to be a version\nmismatch between the bundler and a plugin.\n\nYou are not eliminating the cost. You are deferring it, and adding interest.\n\nThe pipeline was supposed to give you time back. For some software it does. For a\nlot of software it turns out to borrow that time from your future self instead.\n\n## What it costs\n\nThere are real trade-offs. No build pipeline means no tree-shaking for JavaScript, no\nCSS minification by default, no hot module replacement during development.\n\nThe boundary is not \"backend versus frontend.\" smeldr.dev has frontend JavaScript and\nstill ships without a bundler. The boundary is further out than people assume: it is\nthe point where you are building a large interactive single-page application whose\nJavaScript genuinely benefits from tree-shaking, code splitting, and hot module\nreplacement. Static assets served over standard HTTP, and CSS served as-is, do not\nneed any of that.\n\nThe honest version: if you are building a JavaScript-heavy single-page application,\nyou need a bundler and you should have one. The build pipeline exists because it solves\nreal problems for a specific class of software.\n\nThe question is whether your software is in that class, or whether you acquired a\npipeline because that is what software acquires.\n\n## go build is the pipeline\n\nSmeldr compiles with `go build`. The binary embeds templates and static assets via\n`embed.FS`. There is no separate asset pipeline, no manifest, no fingerprinting step.\nThe binary is self-contained.\n\nThis is not a backend-only claim. The project's site, smeldr.dev, runs on Smeldr. It\nhas interactive frontend features and no build pipeline. Two categories of JavaScript,\nneither of which requires a bundler.\n\nThe first is a third-party 3D animation: 110k particles, WebGL2, a file too large\nand complex to write inline. That one is vendored and served as a static file with\n`<script src defer>`. One HTTP request, no transformation.\n\nThe second is everything else: navigation toggle, copy buttons, devlog tag filtering,\na mobile drawer, dark mode with localStorage persistence. These live as plain\nJavaScript inside Go templates. They embed into the binary with the same `embed.FS`\nthat embeds the HTML. No separate asset pipeline, no manifest. The compiler includes\nthem the same way it includes everything else.\n\nTwo different categories of frontend code. The same answer for both.\n\nDeployment is: copy binary, restart process. Or build a container image in one\nDockerfile stage. Or use `fly deploy`. The platform does not matter because the binary\nmakes no assumptions about it.\n\nWhen a dependency updates, `go get` and `go build` tell you immediately if something\nbroke. There is no intermediate tool to negotiate with. The compiler is the build\nsystem, and it has been maintained by Google since 2009.\n\nThe shoulder tension disappears when you do not need most of the pipeline. Not\nbecause pipelines are bad engineering, but because the best tool for the job is often\nthe one you do not have to configure, maintain, and debug at 2am.\n\nSmeldr is open source, written in Go, no build pipeline required.",
"title": "No pipeline, no oil spills"
}