The carcinization of web frameworks

toddle
7 min readMay 15, 2024

Are frameworks converging? Lets explore the topic of carcinization within the web development world, and can we glimpse what the future holds, by looking back?

Who will win, or are all the frameworks more alike than they’d like to admit?

In biological sciences, carcinization refers to the evolutionary phenomenon of non-crab-like crustaceans evolving into forms resembling crabs. A similar story is currently taking place within the web development world. Web frameworks are seemingly undergoing carcinization! — to be clear, the frameworks are not actually developing claws or exoskeletons; rather, they seem to converge towards a similar set of features and are increasingly harder to differentiate conceptually 🦀 🦞

Spoiler alert: I am not suggesting that a theoretically perfect framework exists. Each project is different, and different requirements call for new solutions and tools. However, there might exist a set of near-perfect features that most frameworks will eventually adopt. Perhaps we can catch a glimpse of it in this article.

What happened?

A long long time ago, JavaScript frameworks worked entirely differently from each other; in fact, this was usually their main selling point: “A new, better, but mostly just a different way to develop on the web”. jQuery looked to add utilities to the browsers to develop faster and more cross-browser-friendly websites. Angular wanted to bring the whole suite of Object Oriented Programming (OOP) to the web in an attempt to build more scalable web apps. React proposed using functional principles in few, but sophisticated ways to let developers create web apps using only functions and composition. Other frameworks were based on entirely new foundations, and some simply improved on existing frameworks by making them faster, smaller, or combining different ideas.

Naturally, things evolved… 🧬 Some frameworks gained more traction than others. The best ideas were incorporated into the new frameworks, as well as existing ones. AngularJS moved its focus to angular 2. A simplified version that dropped some of the OOP values. Vue implemented React’s hooks, and React ditched their class components in favor of functions. Even Svelte, once considered the breakout framework, has recently implemented Runes (which mimics signal`, as seen in Solid, toddle, and Preact).

So, what features are we seeing across the board today?

Immutability 🪨

It is somewhat of a controversial topic for programmers. Immutable data-handing is a significant tradeoff in performance vs. developer experience (*DX*). It first seems wasteful to duplicate entire objects and arrays on each simple mutation, so it is no wonder this was not the default for many years. However, by committing to immutability, your app may lack some up-front performance optimizations but make up for it by shipping fewer bugs and having faster development cycles. That being said, immutability has become such a cornerstone in today’s frameworks that we often forget when not to use it.

Many frameworks expect you to pass them purely immutable data. Modifying references may have dangerous and unforeseeable side effects. However, ejecting from immutability in isolation is always acceptable. What your framework doesn’t know, can’t hurt it:

🐢 Slow, but is thoroughly immutable

let arr = []
for(let n = 0; i < 1000; i++) {
arr = […arr, i]
}

🐇 Fast, but is mutating the array directly 😱

let arr = []
for(let n = 0; i < 1000; i++) {
arr.push(i)
}

The second example is much faster and is perfectly fine as long as you write test cases and wrap it in a function to encapsulate the mutable code as a pure function.

Single-directional flow of data 🌊

Two-way data binding was first introduced by the angular team, and it seemed like dark magic 🪄 Too good to be true ✨ Change data in a child component, and the parent would update itself and all of its remaining children — what’s not to like?

In real-world examples, however, this often led to spaghetti code 🍝 where no one truly owns the data. The source of truth, so to speak, existed everywhere and nowhere simultaneously. You could not figure out who corrupted the data when something didn’t work.

With a strict single-directional flow of data, we ensure that data always have one single owner. When something breaks, you simply follow the stream of data to find the culprit 🕵️‍♂️ This forced limitation solved many of the issues haunting web developers. Though, it does require us to be more explicit with our code. Today, almost all frameworks follow a data-down events-up approach.

Signals ⚡

State management is likely the most important concept when making a framework scalable or not. Each time you add a global state variable, you, at minimum, double the complexity of your app in terms of how many theoretical states exist. As an app grows, the state does as well, often with billions of possible states it can be in at any point. When the number of states reaches a critical size, an app becomes nearly impossible to develop. As one changes one part of the app, it statistically breaks at least one other thing somewhere else.

Considering the complexity and importance of handling state, it is no surprise that state management has historically been one of the main differentiators between JavaScript frameworks. Angular has had multiple state systems over its lifetime, and so has Vue and Svelte. React has had multiple systems, along with thousands of compatible state-management libraries. We have gone from two-way data binding to observables to hooks, and now the next big craze: Signals ⚡

However, signals may be more than just the latest craze. While it can initially be tricky to understand signals, they are surprisingly simple and can be implemented in just a few lines of code. Signals support granular reactivity and can be used for global states and updates of the smallest primitives.

Did you know signals are so popular now that native adoption has been proposed? toddle utilizes a custom signal implementation to achieve its fine-grained reactivity and blazing performance!

Server-side rendering 🌐

For years, frameworks proudly presented themselves as client-side-only libraries. A way to avoid having to write server code to render a website. No more PHP or Java, just simple JavaScript. Today, these tiny client-only libraries have grown to become entire ecosystems and are expected to have — you’ve guessed it — Server Side Rendering (SSR).

We have now come full circle 🤦 except it is now often the client-side languages that dictate what the server outputs. You write your app once, and the server emulates the first state of each page and sends it back to you as pure HTML, CSS, and JavaScript. In frameworks like NextJS, you can even write Server Actions that run on the server!

SSR has quickly become mainstream as it offers better SEO and often a faster time to first paint. Today, SSR is a strict requirement for almost any project. Toddle also has full SSR support and can even run complex formulas and fetch APIs on the server to ensure optimal SEO and get content to the screen as fast as possible.

Predictions 🔮

Frameworks may have converged a lot already, but there is still room for innovation. Let us look at some exciting developments in the field that I predict will copied, improved, and implemented in most frameworks.

Resumability

Resumability is an improvement to Server-Side Rendering. It represents an evolution in how applications handle the initial rendering process, offering better performance and user experience. For example, frameworks like React Server Components aim to leverage streaming capabilities to deliver components to the client incrementally, enhancing the user experience by progressively hydrating the UI. Qwik coined the term and has excellent support already.

Resumability will likely become a more prominent feature in the future as developers inevitably seek to optimize performance and improve the scalability of their applications in an increasingly interactive web landscape.

Web components

The adoption of web components seems to always be right around the corner. It is perhaps forever a prediction, but we have recently seen some exciting new browser support for Declarative Shadow DOM and other additions to custom elements. Web components are fantastic for a multitude of reasons. You can build a component in one framework and then drop it into any other App without friction! While not a first-class citizen, SolidJS does support web components, and many other frameworks have partial support. In toddle, components inherit from the web component spec directly and are not only compatible but literally built as web components. Any toddle component can be exported as a web component with no additional setup.

If/when web components become mainstream, it enables a bunch of improvements for all web developers:

  • Try a new framework at no cost! Build one web component and implement it in your existing site as a native element. No need to go from scratch when switching frameworks.
  • Each team in an organization can pick their framework. Components between teams can communicate through the web component standard and compose seamlessly.
  • The inherited island architecture of web components makes it easy to iteratively move a legacy app to a more modern one made in toddle, solid, or Qwik. Convert one component at a time to slowly devour and overtake the old website as islands spread.

A less chaotic world 🌎

It seemed for a while that a new framework was released every week. This probably still holds, but each new framework is much like what we already know as we converge on some basic features that have proven the test of time. If you know Framework A, you also nearly know Framework B. Syntax and a few features may vary, but the core concepts translate easily. If you already know React or Vue, you should give Qwik or toddle a go, as learning them is a breeze.

As a bonus, as frameworks have converged, it has made it more evident for the W3C team which features our browsers need the most. Due to its extreme popularity, nearly all the functionality of jQuery now lives natively in the browser you are currently reading this article. We are now seeing the same with Signals, Declarative Shadow DOM, and more that will enable framework-like capabilities without actually downloading an entire framework. Perhaps the last framework will be no framework, just pure vanilla browser features once the carcinization is complete? 🦀 At toddle, we are excited to experiment with the bleeding edge of browser capabilities. It is truly an exciting time to develop for the web!

Originally published at https://toddle.dev.

--

--

toddle

Next Generation Development platform for unified product teams.