Back to Blog

Rails 8's 'No Build' Philosophy

Back to the Future of Web Development

10 min readLessons from DHH
++

Full disclosure: This post was written by a human (me), polished by an AI (it fixed my grammar and made me sound smarter), then reviewed by me again (to make sure the AI didn't make me sound too smart). Any remaining errors are 100% organic, artisanal, human-made mistakes.

In a world where "npm install" downloads more files than your entire application, Rails 8 takes a radical stance: what if we just... didn't? DHH and the Rails team have built an entire web framework that works without a JavaScript build step. And it's not a regression—it's a return to sanity.

The '90s Deployment Model Was Right

DHH made a provocative point in his Lex Fridman interview: the '90s web development model was actually ideal for developer experience.

"You wrote your PHP, you uploaded it via FTP, and it was live. That's it. That instant feedback loop, that directness—we spent 15 years building complexity to get away from it, only to realize we want it back."

— DHH on Lex Fridman Podcast

The '90s model had problems—no version control, manual uploads, PHP spaghetti. But the core experience was magical: change a file, see the change. No compilation. No bundling. No waiting for Webpack to finish.

What we lost in the "dark ages" wasn't sophistication—it was immediacy. Rails 8 restores that immediacy while keeping the good parts we've developed since.

How "No Build" Actually Works

Import Maps: The Key Enabler

Import maps are a browser feature that lets you control how JavaScript imports resolve—without a bundler. Instead of Webpack resolving import "react"to a bundled file, the browser uses a JSON mapping:

<!-- In your HTML head -->
<script type="importmap">
{
  "imports": {
    "@hotwired/turbo-rails": "https://cdn.jsdelivr.net/npm/@hotwired/turbo@8/+esm",
    "@hotwired/stimulus": "https://cdn.jsdelivr.net/npm/@hotwired/stimulus@3/+esm",
    "lodash": "https://cdn.jsdelivr.net/npm/lodash-es@4/+esm"
  }
}
</script>

<!-- Then in your JS files -->
<script type="module">
  import { Application } from "@hotwired/stimulus";
  import { debounce } from "lodash";

  // Just works! No bundler needed.
</script>

ES Modules Everywhere

Modern browsers natively support ES modules. You can import andexport without any preprocessing:

// app/javascript/controllers/hello_controller.js
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  connect() {
    console.log("Hello from Stimulus!");
  }
}

// No compilation. No bundling. Ship it.

HTTP/2 Makes Small Files Fast

One argument for bundling was "fewer requests = faster." HTTP/2 changed this. With multiplexing, many small files can be just as fast as one large bundle—often faster because browsers can cache individual files and only re-download what changed.

What You Actually Gain

Developer Experience

  • • Instant refresh—no build step
  • • No Webpack configuration
  • • No mysterious compilation errors
  • • What you write is what runs
  • • Debugging shows actual source

Production Simplicity

  • • Fewer moving parts
  • • No build server required
  • • Faster deployments
  • • Better caching
  • • Easier debugging

Reduced Cognitive Load

This is the biggest win. When there's no build step, you don't have to understand:

  • • How Webpack loaders work
  • • Why your source maps aren't generating
  • • Which Babel presets you need
  • • Why hot module replacement stopped working
  • • What "chunk splitting" means

You just write JavaScript. The browser runs it. That's the entire mental model.

When You Still Need a Build Step

Rails 8's no-build approach isn't dogma—it's a default. You can still add build tools when genuinely needed:

  • TypeScript: If you want static typing, you'll need compilation. But consider: do you actually need TypeScript, or are you adding it because "everyone does"?
  • JSX: React's JSX syntax requires transformation. Though with Hotwire (Turbo + Stimulus), you might not need React at all.
  • CSS preprocessing: If you need Sass variables or nesting—though CSS now has native variables and nesting is coming.
  • Legacy browser support: If you must support IE11 (condolences), you need Babel.

The key shift: Build tools become opt-in for specific needs, not a default assumption. Start without them. Add them only when you hit a wall that requires them.

The Hotwire Stack: Modern UX Without SPAs

"But how do you build modern, interactive UIs without React?" Enter Hotwire—Rails' answer to the SPA question:

Turbo: HTML Over the Wire

Instead of fetching JSON and rendering client-side, Turbo fetches HTML and swaps it into the page. The server renders everything. The client just handles navigation and partial updates.

Stimulus: Just Enough JavaScript

For client-side interactivity, Stimulus provides a minimal controller pattern. No virtual DOM, no state management, no hooks. Just connect HTML to JavaScript behavior.

<!-- The HTML drives the behavior -->
<div data-controller="clipboard">
  <input data-clipboard-target="source" value="Copy this text">
  <button data-action="click->clipboard#copy">Copy</button>
</div>

// Minimal JavaScript
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["source"];

  copy() {
    navigator.clipboard.writeText(this.sourceTarget.value);
  }
}

This stack powers Hey.com, Basecamp, and Shopify admin—applications with rich UIs built without SPAs, without client-side routing, without the complexity tax.

The Future Is Optional Complexity

Rails 8's "no build" philosophy isn't about rejecting modern JavaScript—it's about making complexity opt-in. Start simple. Add tooling when you need it. Don't import Facebook's problems.

The browser has caught up. ES modules, import maps, HTTP/2, native CSS features—the platform can handle what required build tools in 2015. We just need the courage to trust it.

The '90s called. They want their instant feedback loop back.

And thanks to Rails 8 and modern browsers, we can finally give it to them—while keeping all the good things we've learned in the meantime. The future of web development isn't more build tools. It's fewer.