Chapter 2: JavaScript Fatigue: How We Got Here

In 2009, you could build a website with Notepad and feel like a god. By 2015, you needed a computer science degree just to display “Hello, World” on a page. This is the story of how we got from <script>alert('hi')</script> to needing seventeen build tools just to start a project.

The Golden Age (2006-2010): When jQuery Ruled the Earth

Remember when learning web development was simple? Here was the entire curriculum:

  1. HTML for structure
  2. CSS for styling
  3. JavaScript (with jQuery) for interactivity
  4. PHP for backend (optional)

That was it. Four technologies. You could learn everything you needed in a weekend and build real, production websites that actual people used.

The entire jQuery library was 32KB. Today, that’s smaller than most websites’ cookie consent banners.

<!DOCTYPE html>
<html>
<head>
  <script src="jquery.js"></script>
  <script>
    $(document).ready(function() {
      $('#button').click(function() {
        $('#result').load('data.html');
      });
    });
  </script>
</head>
<body>
  <button id="button">Click me</button>
  <div id="result"></div>
</body>
</html>

Look at that. No build step. No transpilation. No webpack config. No package.json. You just… wrote code and it worked. Revolutionary concept, I know.

The Great Framework Wars Begin (2010-2013)

Then something happened. Google released Angular. Backbone.js appeared. Ember entered the chat. Suddenly, jQuery wasn’t enough. We needed “architecture” and “patterns” and “separation of concerns.”

The sales pitch was compelling:

  • “Your jQuery code is spaghetti!” (It wasn’t)
  • “You need MVC on the frontend!” (We didn’t)
  • “Two-way data binding will save you!” (It didn’t)
  • “Single Page Applications are the future!” (They were, unfortunately)

Each framework promised to be the last framework you’d ever need. Spoiler alert: it wasn’t.

The Node.js Butterfly Effect (2009-2014)

While the framework wars raged, Ryan Dahl made an innocent decision that would doom us all: he created Node.js, allowing JavaScript to run on servers.

This seemed harmless enough. “JavaScript everywhere!” we cheered. “One language to rule them all!”

What we didn’t realize was that we’d just given frontend developers access to the server ecosystem. And with it, came:

  • Package managers (npm)
  • Build tools (Grunt, Gulp)
  • Module systems (CommonJS, AMD, UMD)
  • Transpilers (CoffeeScript, TypeScript)

Pandora’s box was open.

NPM: The Package Manager That Ate the World

NPM started innocently. “Share code easily!” they said. “Don’t reinvent the wheel!” they promised.

Fast forward to today:

$ npm install react
+ added 296 packages from 178 contributors

296 packages. To use React. That’s 296 potential points of failure, 296 things that could have security vulnerabilities, 296 reasons your build might break on a Tuesday afternoon for no apparent reason.

Remember the left-pad incident of 2016? An 11-line package that padded strings was removed from npm, and half the internet stopped building. Eleven lines:

module.exports = leftpad;
function leftpad (str, len, ch) {
  str = String(str);
  var i = -1;
  if (!ch && ch !== 0) ch = ' ';
  len = len - str.length;
  while (++i < len) {
    str = ch + str;
  }
  return str;
}

This brought down Babel, React, and Node. Because nobody could write a padding function themselves anymore.

Build Tools: A Love Story Gone Wrong

First came Grunt (2012):

// Gruntfile.js - 200 lines to minify some JavaScript
grunt.initConfig({
  pkg: grunt.file.readJSON('package.json'),
  uglify: {
    options: {
      banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
    },
    build: {
      src: 'src/<%= pkg.name %>.js',
      dest: 'build/<%= pkg.name %>.min.js'
    }
  }
});

“Too much configuration!” developers cried.

Then came Gulp (2013):

// gulpfile.js - "Simpler" they said
gulp.task('scripts', function() {
  return gulp.src('src/**/*.js')
    .pipe(sourcemaps.init())
    .pipe(babel())
    .pipe(concat('all.js'))
    .pipe(uglify())
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('dist'));
});

“Still too complicated!” developers wailed.

Then came Webpack (2014):

// webpack.config.js - "This will solve everything!"
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react']
          }
        }
      }
    ]
  },
  // ... 200 more lines of configuration
};

Each tool promised to solve the problems of the previous tool, but somehow made everything more complicated.

The Day HTML Stopped Being Enough

Then React arrived with JSX, and suddenly HTML wasn’t HTML anymore:

// Before React: HTML is HTML
<div class="greeting">Hello!</div>

// After React: HTML is JavaScript pretending to be HTML
<div className="greeting">Hello!</div>

// But wait, it gets worse
<div className={`greeting ${isActive ? 'active' : ''}`}>
  {user && <span>Hello {user.name}!</span>}
</div>

Notice the subtle differences? class became className. Why? Because class is a reserved word in JavaScript. Because now your HTML isn’t HTML, it’s JavaScript cosplaying as HTML.

The Transpilation Nation

But JSX was just the beginning. Soon, we needed:

  • Babel: To use modern JavaScript
  • TypeScript: To add types to JavaScript
  • Sass/Less: Because CSS wasn’t enough
  • PostCSS: To transform the CSS we transformed
  • ESLint: To make sure our code follows rules
  • Prettier: To format the code ESLint approved

Your simple website now required a build pipeline that would make a car factory jealous.

The Great Bundler Migration

Just when you thought you understood Webpack, the community decided Webpack was too slow. Enter:

  • Parcel (2017): “Zero configuration!” (Still needed configuration)
  • Rollup (2015): “Better for libraries!” (But not for apps)
  • Snowpack (2019): “No bundling!” (Some bundling required)
  • Vite (2020): “It’s fast!” (It actually is, but give it time)
  • esbuild (2020): “It’s faster!” (Written in Go, because JavaScript tools being slow is JavaScript’s fault)
  • SWC (2021): “It’s even faster!” (Written in Rust, because Go wasn’t trendy enough)
  • Turbopack (2022): “Webpack’s successor!” (By the creators of Webpack, who apparently learned nothing)
  • Bun (2023): “Replace everything!” (Runtime, bundler, package manager, kitchen sink)

Each new tool promises to solve all your problems. Each new tool creates new problems that the next tool will promise to solve.

The Framework Framework

But wait, React alone wasn’t enough. You needed:

  • Create React App: To start a React project (deprecated)
  • Next.js: For server-side React
  • Gatsby: For static site React
  • Remix: For full-stack React
  • Blitz: For full-stack React (but different)
  • RedwoodJS: For full-stack React (but more different)

Each framework on top of the framework, because apparently React by itself is too simple. It’s frameworks all the way down.

The State Management Saga

React has state, but that wasn’t enough:

  • Flux: Facebook’s pattern (too complicated)
  • Redux: Simplified Flux (still too complicated)
  • MobX: Simpler than Redux (different complicated)
  • Recoil: Facebook’s new thing (experimental forever)
  • Zustand: Actually simple (give it time)
  • Jotai: Atomic state (atoms everywhere)
  • Valtio: Proxy-based (magic scary)
  • XState: State machines (computer science degree required)

Each one promises to make state management “simple” and “intuitive.” None of them are.

The Testing Tribulation

Remember when testing meant opening your website in a browser and clicking around? Now you need:

  • Jest: For unit tests
  • React Testing Library: For component tests
  • Cypress: For E2E tests
  • Playwright: Better E2E tests
  • Vitest: Jest but with Vite
  • Testing Library: Not just for React anymore
  • MSW: To mock your API calls
  • Storybook: To test components in isolation

Your test setup is now more complex than your actual application.

The Modern “Hello World”

Here’s what you need to display “Hello, World” in 2024:

$ npx create-next-app@latest hello-world
$ cd hello-world
$ npm install

This installs approximately 300MB of node_modules. For “Hello, World.”

Your package.json:

{
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "next": "^14.0.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "autoprefixer": "^10.0.0",
    "eslint": "^8.0.0",
    "eslint-config-next": "^14.0.0",
    "postcss": "^8.0.0",
    "tailwindcss": "^3.0.0",
    "typescript": "^5.0.0"
  }
}

Your actual Hello World component:

export default function Home() {
  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-24">
      <h1>Hello, World!</h1>
    </main>
  );
}

Compare this to 2009:

<h1>Hello, World!</h1>

Progress!

The Fatigue Sets In

This is JavaScript fatigue. It’s not that JavaScript is bad. It’s not even that these tools are bad (mostly). It’s that there are so many of them, changing so quickly, each claiming to be essential, that you can never actually build anything because you’re too busy learning the new tool that makes the previous tool obsolete.

By the time you learn React, you need to learn Next.js. By the time you understand Redux, everyone’s moved to Zustand. By the time you master Webpack, everyone’s using Vite.

It’s exhausting. It’s demoralizing. It’s… modern web development.

The Reality Check

Here’s the dirty secret: most websites don’t need any of this. The vast majority of web applications could be built with:

  • HTML
  • CSS
  • A sprinkle of vanilla JavaScript
  • A simple backend

But that’s not the React way. The React way is to turn every simple problem into a complex engineering challenge, then solve that challenge with more complexity.

In Defense of Complexity (Sort Of)

To be fair, some of this complexity serves a purpose:

  • Type safety helps catch bugs
  • Build tools enable better developer experience
  • Testing ensures reliability
  • Bundling improves performance

But we’ve lost all sense of proportion. We’re using sledgehammers to hang picture frames, wondering why our walls keep breaking.

The Path Forward (Or Backward?)

The good news? There’s a movement back toward simplicity:

  • HTML Web Components: Standards-based components
  • Alpine.js: Minimal framework for simple interactivity
  • HTMX: HTML-driven development
  • Vanilla JavaScript: It’s actually good now!

But you’re not here to learn those. You’re here to learn React. Because that’s what the job market demands. Because that’s what your team chose. Because resistance is futile.

Accepting Your Fate

JavaScript fatigue is real, and React is both a symptom and a cause. You’ll spend more time configuring tools than writing features. You’ll debug build processes more than application logic. You’ll question your career choices at least once a week.

But you’ll also build things. Complex, over-engineered things that could have been simple, but things nonetheless. And in the modern web development world, that’s what counts.

Welcome to the club. Your node_modules folder is over there. It’s the one taking up 2GB of disk space. For a todo app.

In the next chapter, we’ll dive into the Virtual DOM - React’s special sauce that promises performance but delivers complexity. Spoiler alert: it’s not actually faster than the real DOM, but we’ll pretend it is because that’s what we do now.

Pour yourself another coffee. You’ll need it.