Why does displaying a button that updates a counter require a JavaScript framework, a build system, a bundler, and a node_modules folder heavier than the Apollo 11 guidance computer?

The answer is simple, and we've been avoiding it for thirty years: the web is broken.

Not for documents — it's fine for that. It's broken for applications. And applications are what we're being hired to build.

The Retrofit Stack

The web was designed for documents. Linked documents. Tim Berners-Lee — the British computer scientist who invented the World Wide Web in 1989 while working at CERN — wanted physicists to share papers with clickable references. That's it.

HTML was never meant to be an application framework. It was meant to mark up text. You could style it inline — <font color="blue">, bgcolor attributes, nested tables for layout—but mixing content and presentation made the markup unreadable. So we bolted on CSS to separate the two. When we wanted documents to do things, we bolted on JavaScript—a language famously designed in ten days.

Then we wanted MORE! Real applications. State management. Components. Routing. So we bolted on jQuery, then Backbone, then Angular, then React, then Vue, then Svelte, then Solid, then Qwik, then whatever launched this week.

Each layer is a patch for the failures of the layer beneath it, and every addition reveals complexity that couldn't be abstracted away.

Alan Kay, the computer scientist who invented object-oriented programming and pioneered the graphical user interface, put it bluntly:

"The Internet was done so well that most people think of it as a natural resource like the Pacific Ocean, rather than something that was man-made. When was the last time a technology with a scale like that was so error-free? The Web, in comparison, is a joke. The Web was done by amateurs."[¹]

The Internet is the plumbing — TCP/IP, packet routing, DNS — the infrastructure that lets computers find and talk to each other. It was engineered carefully over decades and has run continuously since 1969. The web is an application that runs on top of the Internet: HTTP, HTML, browsers. Kay's point is that the underlying network is a marvel; the thing we built on top of it is amateur hour.

Kay put his finger on the root problem: "You want it to be a mini-operating system, and the people who did the browser mistook it as an application." The browser should have been a platform for running software. Instead, it was built to display documents — and we've been retrofitting ever since.[²]

The Icon Test

Want to see the dysfunction in miniature? Try displaying a colored icon.

React (using react-icons):

import { FaStar } from 'react-icons/fa';
// Hope the SVG uses currentColor internally
<span style={{ color: 'blue' }}>
  <FaStar />
</span>

But if the SVG has hardcoded fills? You'll need a custom component, SVGR in your build pipeline, or inline the entire SVG and manually override attributes.

Vue:

<template>
  <svg-icon name="star" class="icon-blue" />
</template>
<style>
.icon-blue {
  color: blue; /* Maybe works? Depends on the SVG */
}
.icon-blue >>> path {
  fill: currentColor; /* Deep selector, deprecated */
}
</style>

Angular:

import { DomSanitizer } from '@angular/platform-browser';
// Bypass security to inline SVG
this.iconHtml = this.sanitizer.bypassSecurityTrustHtml(svgString);
/* Pierce encapsulation to style it */
::ng-deep svg path {
  fill: blue !important;
}

Web Components:

// Shadow DOM makes external styling harder, not easier
class MyIcon extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    // Must expose CSS custom properties explicitly
    // or use ::part() selectors
    // or just give up on encapsulation entirely
  }
}

Four ecosystems. Four different rituals. All fragile. All requiring you to understand implementation details of how SVGs work, how CSS specificity works, how your framework's encapsulation works, and how they all fail to work together.

Now here's SwiftUI:

Image(systemName: "star.fill")
    .foregroundColor(.blue)

And Jetpack Compose (Android):

Icon(
    Icons.Default.Star,
    tint = Color.Blue
)

Two lines. No build step. No CSS. No specificity wars. No security bypasses. No encapsulation workarounds. Color is a property because of course it is — these platforms were designed for building applications.

This isn't Apple or Google being more innovative. It's what happens when you design for applications from the start instead of retrofitting a document viewer.

The Standard That Wasn't

If you've been in this industry long enough, you remember the promise of Web Components.

The W3C looked at the framework chaos — React vs. Angular vs. Vue vs. Ember vs. whatever — and said: "We'll fix this. We'll create a standard. Custom Elements. Shadow DOM. HTML Templates. Native components, blessed by the spec itself."

It was supposed to end the framework wars. A standard to replace them all.

You know what happened? Web Components became another option.

React developers didn't switch. Vue kept growing. Svelte emerged. The frameworks didn't die — they just added Web Components interop as a bullet point in their documentation. The "standard" became one more choice in an already overwhelming landscape.

When was the last time a technology with a scale like that was so error-free? The Web, in comparison, is a joke. The Web was done by amateurs."[¹]

Why? Because Web Components didn't solve the actual problem. The actual problem isn't "which component model" — it's that we're building applications inside a document viewer that has no native concept of components, state, or interactivity. Web Components bolted a component model onto a platform that wasn't designed for one. Sound familiar?

The pattern repeats: identify a symptom, patch it, declare victory, watch the churn continue.

The Churn Is the Evidence

Here's the argument you'll hear: "This is just how software evolves. We improve tools over time. Iteration is healthy."

It's wrong.

Imagine if the entire construction industry had decided to build skyscrapers by stacking garden sheds. Each year, there'd be a new "shed-stacking framework" — better brackets, improved load distribution, innovative mortar. Conferences would debate vertical-shed versus horizontal-shed architecture. No one would ask: why are we stacking sheds?

That's web development.

Look at the framework genealogy. jQuery emerged because raw DOM manipulation was painful. Backbone and Angular emerged because jQuery spaghetti became unmaintainable. React emerged because Angular was too complex — it offered a simpler mental model with one-way data flow. Vue emerged because Evan You wanted "the good parts of Angular" without the heavy machinery. Svelte emerged because Rich Harris found React's virtual DOM runtime overhead unnecessary. Solid emerged because Ryan Carniato wanted React's model without React's re-rendering costs.

Each framework is a response to the previous framework's complexity. Not the platform's. They're patching patches. It's an infinite regress of symptom management, where each "solution" introduces new complexity that spawns the next "solution."[⁷]

Fred Brooks, in his landmark 1986 paper "No Silver Bullet," distinguished between essential complexity (inherent to the problem you're solving) and accidental complexity (the mess we create with our tools and choices).[³] The web stack is a monument to accidental complexity. The problem of "display a colored icon" has trivial essential complexity. The four different framework incantations required to solve it? Pure accident.

Rich Hickey, creator of the Clojure programming language, sharpened this point in his influential talk "Simple Made Easy." He observed that developers chronically confuse easy (familiar, at hand) with simple (not entangled). We reach for complex tools because they're familiar, then wonder why our systems become unmaintainable. "If you focus on ease," Hickey warned, "you will be able to go as fast as possible from the beginning of the race, but the complexity will eventually kill you."[⁴]

Consider what we've normalized:

  • A button that updates a number requires shipping hundreds of kilobytes of JavaScript.
  • "Hydration" — sending your UI twice, once as HTML, once as JS — is considered standard architecture
  • Build toolchains more complex than the applications themselves.
  • An entire discipline called "web performance" is dedicated to making the platform do less badly what it shouldn't be doing in the first place

This isn't normal. We've just pretended it is for so long that we've forgotten to be appalled.

What You're Missing If You've Only Known the Web

If you learned to code by building websites — and statistically, you probably did — you might not realize how strange this all is. You've never experienced anything else. The dysfunction is your baseline.

So let me describe a different world.

In SwiftUI or Jetpack Compose, you declare your UI. State is built into the language — @State, @Binding, @Observable in Swift; remember, mutableStateOf in Kotlin. When state changes, the UI updates. No virtual DOM diffing. No hooks rules to memorize. No useEffect dependency arrays to debug at 2am.

Components are just structs or functions. You don't import a framework to have components — they're how the platform works. Styling isn't a separate language bolted on afterward; it's methods on your views. Layout isn't CSS Grid vs. Flexbox vs. floats vs. position: absolute hacks; it's VStack, HStack, ZStack—containers that do what they say.

There's no build step. No webpack config. No tree-shaking. No bundle splitting. No node_modules black hole. You write code. You run it. It runs.

This isn't utopia — SwiftUI has its own quirks, Compose has its own learning curve. But notice what's absent: the constant sense that you're fighting the platform. The pile of abstractions between your intention and the machine. The tooling complexity that exists purely to work around platform limitations.

That absence is what "designed for purpose" feels like. Most web developers have never experienced it.

The Normalization Trap

Here's the real danger: as long as we treat this as normal, we can't escape it.

The philosopher Thomas Kuhn studied how scientific revolutions happen — and why they take so long.[⁵] His key insight: scientists working within a paradigm literally cannot see its limitations. The paradigm defines what counts as a legitimate problem and what counts as a valid solution. Anomalies get explained away or ignored. Only when anomalies pile up to crisis levels does the paradigm crack — and even then, the old guard rarely converts. They just eventually die off.

Kuhn was describing physics, but he could have been describing web development. The framework churn, the build tool explosion, the performance hacks — these are anomalies. They're signs that the paradigm is broken. But because we're inside the paradigm, we interpret them as normal problems requiring normal solutions. React Server Components. Islands Architecture. Resumability. Each is clever. Each is also an elaborate workaround for a document viewer that never should have become an application platform.

The philosopher Slavoj Žižek, drawing on Alain Badiou, identified this pattern in late capitalism: a "noisily marketed 'perpetual revolution'" that is really just "the cliché 'the more things change, the more they stay the same.'" Badiou called it "the obsession of novelty and the perpetual renovation of forms."[⁶]

That's the JavaScript ecosystem in a sentence. React to Vue to Svelte to Solid — perpetual renovation of forms, all leaving the underlying dysfunction intact.

The psychoanalyst Erich Fromm made a related observation about reform itself: "There is reform and reform; reform can be radical, that is, going to the roots, or it can be superficial, trying to patch up symptoms without touching the causes. Reform which is not radical, in this sense, never accomplishes its ends and eventually ends up in the opposite direction."[⁸]

Framework churn is superficial reform. It patches symptoms — verbosity, performance, developer experience — without touching the cause: we're building applications in a document viewer.

The debates we have — React vs. Svelte, SSR vs. CSR, monorepos vs. polyrepos — are optimization debates within broken constraints. They foreclose the more important question: should we accept these constraints at all?

What Now?

I'm not here to pitch a replacement. That would be premature — and probably wrong. The right move isn't to rush toward a solution that imports assumptions from the broken paradigm.

But I'll acknowledge the hard problem: the web's dysfunction and its openness may not be separable. Every "designed for purpose" alternative is controlled by a platform owner — not just the language, but the distribution channel. Swift is open source, but the App Store is not. Kotlin is open source, but Google Play controls Android distribution. The web, for all its mess, is genuinely vendor-neutral: anyone can run a server, anyone can build a browser, no one owns the platform.

Any successor to the web must solve for both: purpose-built design and vendor-neutral distribution. That's the problem no one has cracked.

But we can't even begin to crack it while we're pretending the current situation is acceptable.

The right move is simpler: stop pretending this is acceptable.

Name it. The web is a document viewer that we've spent three decades torturing into an application platform. The constant churn isn't progress. The framework wars aren't innovation. They're symptoms.

Once we actually acknowledge the problem — once we stop treating the retrofit stack as natural — then we create space for genuinely different thinking. Not "what's the best JavaScript framework" but "why are we writing JavaScript at all." Not "how do we optimize hydration" but "why does hydration exist."

What would it look like to have universal, open, linkable software — the web's great gift — without the retrofit stack? How do you get vendor-neutral distribution without browser-as-document-viewer? These are the questions we can't even ask while we're busy debating hydration strategies.

The web gave us something extraordinary: universal, open, linkable software that runs everywhere. That matters. But we can honor what the web gave us while admitting that the current stack is a mess.

The first step is saying it out loud.

The web is broken. Stop pretending it's not.

[¹]: Alan Kay, "A Conversation with Alan Kay," ACM Queue, Vol. 2, №9, December/January 2004–2005. https://queue.acm.org/detail.cfm?id=1039523

[²]: Alan Kay, quoted in Don Hopkins, "Alan Kay on 'Should web browsers have stuck to being document viewers?'" Medium, May 2024. https://donhopkins.medium.com/alan-kay-on-should-web-browsers-have-stuck-to-being-document-viewers-and-a-discussion-of-news-5cb92c7b3445

[³]: Frederick P. Brooks Jr., "No Silver Bullet — Essence and Accident in Software Engineering," Proceedings of the IFIP Tenth World Computing Conference, 1986. https://worrydream.com/refs/Brooks_1986_-_No_Silver_Bullet.pdf

[⁴]: Rich Hickey, "Simple Made Easy," keynote at Strange Loop Conference, 2011. https://www.infoq.com/presentations/Simple-Made-Easy/

[⁵]: Thomas S. Kuhn, The Structure of Scientific Revolutions (University of Chicago Press, 1962). Kuhn argued that scientific progress is not cumulative but rather proceeds through paradigm shifts — revolutionary changes in the fundamental assumptions that define legitimate problems and solutions within a field.

[⁶]: Slavoj Žižek, In Defense of Lost Causes (Verso, 2008), discussing Alain Badiou's concept of false eventalization and the ideology of perpetual novelty.

[⁷]: The framework genealogy is well-documented. Evan You has spoken extensively about creating Vue as a response to Angular's complexity. Rich Harris created Svelte explicitly to eliminate the virtual DOM overhead. Ryan Carniato built Solid to solve React's re-rendering performance issues while keeping its mental model.

[⁸]: Erich Fromm, The Sane Society (Holt, Rinehart and Winston, 1955). Fromm's distinction between radical reform (addressing root causes) and superficial reform (patching symptoms) applies directly to the pattern of framework churn in web development.