Back to Blog

JavaScript's Dark Ages

How We Lost Our Way (2010-2020)

11 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.

DHH called it the "dark ages" of JavaScript—the decade where web development went from simple and accessible to byzantine and exhausting. A period where setting up a build system took longer than building features, and where the average project'snode_modules folder could hold more files than the entire Windows 95 operating system.

The Descent into Complexity

Remember when web development was simple? You wrote HTML. You added some CSS. Maybe a sprinkle of jQuery. You uploaded files via FTP. Done. Users saw your changes instantly.

Then, around 2010, something shifted. JavaScript "grew up" and decided simplicity was for amateurs. The industry embraced a cascade of complexity:

2010:Backbone.js arrives. "We need structure!"
2012:Angular.js explodes. "Directives everywhere!"
2013:React launches. "Virtual DOM! Components!"
2014:Gulp, Grunt, npm scripts. Build tools multiply.
2015:ES6 arrives. Now you need Babel.
2016:Webpack becomes mandatory. "Configure all the things!"
2017-2020:Redux, TypeScript, CSS-in-JS, GraphQL, Next.js...

Each tool solved a real problem. But together, they created a monster. A simple "Hello World" required downloading hundreds of megabytes of dependencies.

The Symptoms of the Dark Ages

"npm install" Became a Full-Time Job

DHH highlighted this absurdity: you'd start a new project, run npm install, and watch thousands of packages download. Each with its own dependencies. Each with potential security vulnerabilities. Each with breaking changes lurking in future updates.

"Looking at a node_modules folder and trying to understand what's actually in there—it's like archaeology. You have no idea what civilization built these ruins or why."

Framework Churn Burned Everyone Out

Learn Angular? It's obsolete—Angular 2 is completely different. Learn React class components? Hooks are the new way. Master Webpack 3? Time for Webpack 4, with a totally new configuration format.

Developers spent more time keeping up with tooling than building products. The knowledge half-life collapsed. What you learned last year was already outdated.

The Build Step Became Mandatory

Remember refreshing the browser and seeing changes? Now you needed a dev server, hot module replacement, and a Webpack configuration file that looked like tax code. The feedback loop that made web development accessible stretched from seconds to minutes.

// A "simple" webpack.config.js circa 2018
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
          },
        },
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
      // ... 100 more lines of configuration
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' }),
    new MiniCssExtractPlugin(),
    // ... more plugins
  ],
  optimization: {
    splitChunks: { chunks: 'all' },
  },
  devServer: {
    hot: true,
    port: 3000,
  },
};

Why Did This Happen?

DHH offered a compelling explanation: Facebook's organizational complexity leaked into their tools.

"When you have 15 specialized roles touching a single feature—a frontend engineer, a JavaScript specialist, a CSS expert, a designer, a PM, a data engineer—you need tooling that mediates between them. But most teams aren't Facebook. They don't have 15 specialists. They have 3 generalists."

— DHH on Lex Fridman Podcast

The industry imported Facebook's solutions for Facebook's problems. React solved real challenges at Facebook's scale. GraphQL made sense for Facebook's data complexity. But a startup with 5 engineers building a CRUD app? They didn't need any of it.

Yet the tools became "best practices." If you weren't using them, you weren't serious. Never mind that they added months to development timelines and required constant maintenance.

The Dawn After the Dark Ages

Around 2020, something shifted. Browser capabilities improved dramatically:

  • ES Modules in browsers: No more bundlers required for basic projects
  • HTTP/2: Multiple small files became efficient
  • CSS improvements: Variables, Grid, Flexbox—no preprocessors needed
  • Modern JavaScript: async/await, optional chaining, nullish coalescing

Suddenly, the preprocessing that defined the dark ages became... optional. You could write modern JavaScript, modern CSS, and ship it directly to browsers. Import maps let you manage dependencies without bundlers.

DHH and the Rails team embraced this with Rails 8's "no build" philosophy. The default Rails setup now works without any JavaScript preprocessing. You can still add it if you need it—but you don't have to.

Lessons from the Dark Ages

1. Complexity Has Compound Costs

Each tool added seems cheap—just one more dependency. But dependencies compound. They interact, conflict, and require updates. A hundred simple tools create exponential complexity.

2. "Best Practices" Often Aren't

What works for Facebook doesn't work for your 5-person startup. Question every "you should use X" with "why, specifically, for my situation?"

3. Simplicity Requires Courage

Saying "we don't need React" in 2017 took courage. The industry consensus was overwhelming. But the teams that resisted often shipped faster and with less pain.

4. The Platform Usually Catches Up

Browsers today can do what required complex tooling in 2015. Before adding dependencies, ask: "Will the platform support this natively soon?"

The Path Forward

We're not fully out of the dark ages. Many projects still carry the legacy of decade-old architectural decisions. But the escape path is clear: question every dependency, embrace browser capabilities, and remember that "npm install" should be a last resort, not a first instinct.

The developers who learned during the dark ages carry scars—and wisdom. They know the cost of complexity intimately. They're often the ones pushing hardest for simplicity now.

The best JavaScript is often no JavaScript at all.

And when you do need JavaScript, the best tooling is often the tooling you don't need to configure. The dark ages taught us that simplicity isn't primitive—it's sophisticated. It's the hardest thing to achieve.