My skill list has jQuery UI and Astro on it. Those are seventeen years apart, and both of them
paid for dinner.
I have been writing JavaScript professionally since 2012. Long enough to have shipped
production code in CoffeeScript, to have argued about AMD modules, and to have watched three
separate generations of tooling declare the previous one obsolete. Here is what each era
actually solved — and the small number of lessons that outlived it.
jQuery, and the problem it really fixed
It is fashionable to be condescending about jQuery. It should not be. jQuery existed because
the DOM API was genuinely hostile and browsers genuinely disagreed with each other, and it
fixed both so thoroughly that it fixed itself out of a job. querySelectorAll is jQuery’s
selector engine, standardised. fetch is $.ajax, standardised.
What jQuery did not have was a way to answer what is on screen right now. State lived in the
DOM. You did not have a user object with an isLoggedIn flag; you had a .logged-in class on
body, and the truth was wherever the last handler left it. Every jQuery codebase past a
certain size fails in the same way: two handlers disagree about the current state, and there is
no single place to look.
The lesson that survived: the DOM is an output, not a database. Everything since —
Backbone, Angular, React — is a different answer to that one sentence.
Backbone, Require.js, CoffeeScript
Backbone was the first widely-used answer: put the state in a model, let the view listen. It
was right about the diagnosis and left the hardest part to you — when a model changed, you
decided what to re-render, and getting that wrong meant either a stale screen or a full
repaint.
Require.js solved modules before the language had them. AMD’s ceremony looks absurd now, but
the alternative was thirty script tags in a load-bearing order.
CoffeeScript is the one I am most nostalgic about and least sad to see go. It was a bet that
JavaScript’s syntax was the problem, and it won its argument so completely that the language
absorbed the wins — arrow functions, classes, destructuring, template strings, for...of.
Every one of those was CoffeeScript first.
The lesson that survived: a tool that fixes a language gap has a shelf life ending the day
the language closes the gap. That is not failure; it is the mechanism by which languages
improve. Worth remembering the next time something feels permanent.
AngularJS to Angular: the rewrite that was correct
AngularJS gave you two-way binding and a digest cycle, and for a while it felt like magic. The
bill arrived as $scope. Nobody could say with confidence where a value came from, and
performance work meant reasoning about how many times a dirty-check would run.
Angular 2 broke everything, and it was right to. Two-way binding by default was the mistake;
one-way data flow with explicit outputs is the correction, and it is the same correction React
made from the other direction.
The lesson that survived: implicit magic scales badly. Any framework feature that makes it
hard to answer “why did this change?” will eventually cost more than it saved.
React, and why it stuck
React’s actual contribution was not the virtual DOM — that is an implementation detail, and one
several frameworks have since discarded. It was UI = f(state): describe what the screen
should look like for a given state and stop writing transitions between states by hand.
Every bug class from the jQuery era — stale screens, two handlers disagreeing, the DOM as
source of truth — is defined out of existence by that model rather than fixed. That is why it
won, and why hooks, signals, and every “React but faster” project keep the same core idea.
React Native, and the caveat
At Yoco I built core features of a flagship mobile app in React Native and led the team doing
it. It delivers on the promise: one codebase, real native components, and a web engineer can
be productive in it quickly.
The caveat is worth stating plainly, because it is where teams get hurt. React Native is a
mobile project. The parts that consume your time are the parts a web background did not
prepare you for: list virtualisation with real data, memory on a four-year-old Android device,
navigation state that survives the OS killing your process, the store review cycle. Being able
to write the components is maybe a third of shipping the app.
The lesson: “you already know React” gets you through the door and about a third of the
way across the room.
The build chain: Grunt, Gulp, Webpack, Vite
I have written config for all of them.
Grunt was tasks as configuration. Gulp was tasks as streams — genuinely nicer, and it lost
anyway, because the problem moved. Webpack won by understanding the actual question: not “run
these tasks” but “here is a module graph, produce bundles”. Everything since — Rollup, esbuild,
Vite — is that idea, faster.
The lesson: the fastest build is the one that does not run. The most valuable thing modern
tooling did was make dev builds incremental and native-ESM. Bundle size discipline came second,
and it came from the same place.
What actually transferred
Eleven years, seven or eight complete rewrites of the ecosystem. What actually carried over:
- The DOM is an output. Every framework since 2010 is a different answer to that.
- Explicit beats implicit, every time, at every layer.
- A tool that patches a language gap dies when the gap closes. Prefer the ones that patch a
problem.
- The abstraction you build will be wrong in a way you cannot predict. Which is an argument
for small, replaceable pieces, not for cleverness.
- Fundamentals compound and frameworks do not. HTTP, the event loop, the box model, how a
browser paints. Every hour there paid off across all eleven years; every hour on a specific
framework’s API paid off for about three.
I do not think the churn is as bad as it is made out to be. Most of it has been the same
handful of ideas arriving with better ergonomics. If you learned why $scope was painful, you
already understood why prop drilling is painful, and you will understand whatever replaces
signals.
The syntax is what changes. The reasons are remarkably stable.