We Are Building Websites with Wrong Frameworks

10 min read Original article ↗

Resti Guay

The Hidden Crisis in Modern Web Development

Why our pursuit of features has led us astray from simplicity, and what we can do about it

5 min read • Web Development • Software Engineering

We need to talk about the elephant in the room: modern web development has become unnecessarily complex. While we’ve been chasing the latest features and paradigms, we’ve lost sight of what actually matters — building websites that work reliably, load quickly, and can be maintained by real development teams.

The frameworks we’ve chosen aren’t just complex; they’re fundamentally misaligned with how the web actually works.

The Complexity Trap We’ve Built for Ourselves

Let’s be brutally honest about what building a “simple” website looks like today. When you want to create a basic interactive webpage, you don’t just write HTML, CSS, and JavaScript anymore. Instead, you embark on a journey that looks something like this:

First, you install your framework of choice. But that framework doesn’t work alone — it needs a build system. The build system needs plugins. The plugins need configuration files. The configuration files need more dependencies. Before you know it, you’re managing 78 different packages just to render “Hello World” in a browser.

For perspective, the entire jQuery library — which powered millions of websites for over a decade and enabled the interactive web as we know it — is smaller than most modern framework’s dependency list. Let that sink in for a moment.

We’ve normalized this madness. We’ve convinced ourselves that 200KB+ JavaScript bundles, complex build processes, and dozens of configuration files are the “modern” way to build websites. We treat this complexity as inevitable, even desirable, because it signals that we’re using “professional” tools.

But what if we’re wrong? What if this complexity isn’t a necessary evil, but just evil?

The False Promise of Magical Abstractions

Framework advocates will tell you that complexity is worth it because frameworks provide “powerful abstractions.” This sounds reasonable until you examine what these abstractions actually cost us in real projects with real deadlines and real human beings trying to get work done.

Consider the layers of abstraction in a typical modern web project. You write code in JSX, which gets transformed by Babel into JavaScript that looks nothing like what you wrote. That JavaScript gets bundled by Webpack using rules you probably don’t fully understand. The bundle gets optimized by tools that make assumptions about how your code will be used. Finally, a runtime framework interprets your transformed, bundled, optimized code and decides what to actually show the user.

At each layer, things can go wrong in ways that are difficult to diagnose. When they do go wrong, you’re not just debugging your code — you’re debugging the interaction between your code, the framework, the build system, the state management library, and potentially a dozen other moving parts you didn’t even know existed.

The Build Complexity Mountain

Modern build systems have become so complex that companies hire dedicated “build engineers” whose entire job is maintaining the machinery that turns source code into websites. Think about how absurd this is: we need specialists just to make our tools work.

These build systems promise to make development easier, but they actually make it harder in subtle ways. Hot module replacement works until it doesn’t, and when it breaks, you lose hours figuring out why your changes aren’t showing up. Source maps help you debug code that looks nothing like what you wrote, which is only necessary because the build system transformed your readable code into something incomprehensible.

The Runtime Complexity Beast

Then there’s the runtime complexity. Modern frameworks run sophisticated algorithms on every state change, reconciling virtual representations of the DOM with the actual DOM. They manage component lifecycles with subtle timing dependencies. They provide hook systems that require expert knowledge to use efficiently and avoid performance pitfalls.

This complexity exists to solve problems that the frameworks themselves created. React’s useMemo, useCallback, and React.memo exist precisely because React's default behavior is inefficient. You need these optimization techniques because the framework does unnecessary work by default.

What Complexity Actually Costs Your Team

Here’s what all this complexity costs in terms that matter to real projects and real people:

The Onboarding Nightmare

A new developer joining a React project needs to understand JSX syntax, the component model, hooks and their dependency arrays, state management patterns, the build system, testing frameworks, routing libraries, and styling solutions. That’s weeks or months of ramp-up time before they can make meaningful contributions.

Compare this to a vanilla JavaScript project where someone with basic web development knowledge can start contributing immediately. They can read the HTML, understand the CSS, and follow the JavaScript logic without learning framework-specific abstractions.

The Debugging Black Hole

When something breaks in a complex framework setup, the debugging process becomes exponentially more difficult. You’re not just debugging your logic — you’re debugging the interaction between layers of abstraction you don’t control.

I’ve witnessed teams spend entire sprints tracking down issues that turned out to be version incompatibilities between packages they didn’t even know they were using. The error messages are often cryptic because they originate from deep within framework internals, not from your actual business logic.

The Performance Paradox

Frameworks promise good performance out of the box, but they only deliver it if you become an expert in framework-specific optimization techniques. The “fast by default” promise falls apart as soon as you try to build anything non-trivial.

Meanwhile, vanilla JavaScript is genuinely fast by default. When you manipulate the DOM directly, you know exactly what work is being done. When you manage state explicitly, you control exactly when updates happen. There’s no hidden reconciliation algorithm deciding whether your updates are worth processing.

The Maintenance Time Bomb

Framework projects accumulate technical debt faster than simple projects because complexity compounds over time. Every dependency is a potential security vulnerability that needs monitoring. Every abstraction layer is a potential point of failure. Every framework update is a potential breaking change that could require significant refactoring.

Teams often find themselves trapped by their framework choices. Migration becomes impossible because too much code depends on framework-specific patterns. You’re locked in, forced to follow the framework’s evolution whether it aligns with your needs or not.

The Web Platform is Better Than We Give It Credit For

Here’s a perspective that might surprise you: the web platform itself is more capable and elegant than the frameworks we’ve built on top of it. Modern browsers provide a rich set of features that can handle most web development needs without additional abstractions.

The platform gives us Web Components for creating reusable UI elements with true encapsulation. CSS Grid and Flexbox provide layout capabilities that previously required complex JavaScript solutions. ES Modules offer code organization without build tools. The Fetch API handles network requests elegantly. Local Storage and IndexedDB provide data persistence. Web Workers enable background processing. Service Workers power offline functionality.

You can build sophisticated, interactive web applications using just these platform features. The frameworks aren’t filling gaps in the platform — they’re replacing platform features with more complex alternatives that require you to learn framework-specific APIs instead of web standards.

When you build on web standards, your knowledge transfers between projects. When you build on framework abstractions, your knowledge becomes trapped within that framework’s ecosystem.

What Simplicity Actually Looks Like in Practice

Simple doesn’t mean primitive or unsophisticated. Simple means you can understand what your code is doing, debug it when it breaks, and modify it when requirements change. Simple means the complexity in your system is essential complexity that solves real problems, not accidental complexity that exists because of your tools.

A simple approach prioritizes transparency over magic. If state management requires three lines of explicit code instead of one line of framework magic, that’s often a better trade-off. The explicit version is debuggable, modifiable, and understandable by anyone who reads it.

Simple systems build on standards that will outlast any particular framework. HTML, CSS, and JavaScript aren’t going anywhere. They’ve been stable for decades and will continue to be stable for decades more. Framework APIs, on the other hand, change with every major version, forcing you to constantly relearn how to accomplish the same tasks.

Simple systems make dependencies explicit rather than hiding them behind abstractions. If your component needs data, it should receive that data explicitly through parameters rather than reaching into global state or context systems that make dependencies invisible and testing difficult.

The Path Forward: Choosing Complexity Wisely

This doesn’t mean we should abandon all abstractions and write everything in assembly language. It means we should treat complexity as a finite resource and spend it carefully on solving real problems rather than framework problems.

Before reaching for a framework, ask yourself: What specific problem am I trying to solve? Could I solve it with vanilla JavaScript and web standards? What’s the smallest possible solution that would work? Often, the answer is simpler than you think.

Start measuring the things that actually matter: time to first render, bundle size, dependency count, build time, and new developer onboarding time. These metrics tell you the real cost of your architectural decisions. A framework that reduces lines of code but increases build time and bundle size isn’t actually making your project simpler — it’s just moving complexity around.

Embrace progressive enhancement as a development philosophy. Start with HTML that works without JavaScript, then add CSS for styling, then add JavaScript for interactivity. This approach naturally leads to simpler, more resilient solutions because it forces you to solve problems in the order of their importance.

Choose boring technology whenever possible. Dan McKinley’s principle of “Choose Boring Technology” applies perfectly to frontend development. Proven, stable technologies are usually more valuable than cutting-edge frameworks that might be abandoned or radically changed in a few years.

The Uncomfortable Truth About What We Actually Need

The uncomfortable truth is that most websites don’t need the complexity we’ve imposed on them. A blog doesn’t need a virtual DOM. A marketing site doesn’t need state management. A simple dashboard doesn’t need a framework at all.

We’ve convinced ourselves that complexity is inevitable, but it’s not. It’s a choice we make with every architectural decision, every dependency we add, every abstraction layer we introduce.

Look at the websites that have stood the test of time — Wikipedia, Hacker News, Berkshire Hathaway. They’re notable for their simplicity and reliability, not for their framework choice. They prioritize content delivery, accessibility, and performance over architectural sophistication.

Conclusion: Simplicity as a Competitive Advantage

We’ve been building websites with the wrong frameworks not because the frameworks are technically inadequate, but because they optimize for the wrong outcomes. They optimize for developer experience during the first week of a project, not the developer experience during the 100th week. They optimize for feature velocity in demos, not long-term maintainability in real projects. They optimize for framework adoption, not web platform advancement.

The next time you start a project, consider taking a radically different approach. Start simple. Add complexity only when you can measure its benefits. Question the tools that promise to make things easier but require expert knowledge to use effectively.

The web platform is more powerful and elegant than ever. Maybe it’s time we started using it directly instead of through layers of abstraction that obscure its power and add unnecessary complexity to our projects.

Your future self — and your future team members — will thank you for choosing simplicity over complexity, transparency over magic, and standards over frameworks. In a world where everyone is adding layers of abstraction, simplicity becomes a competitive advantage.

What’s your experience with framework complexity? Have you found ways to simplify your development process? I’d love to hear your thoughts and experiences in the comments below.