There is a question I have been circling for most of my career: how do we ship TypeScript and web technologies at scale?
Not how do we start a project. Starting has never been easier. I mean how do we build something that survives contact with production, with a team, and with five years of maintenance, without collapsing under the weight of its own conventions.
I keep coming back to it because I keep writing the same plumbing: the same hardening of a codebase before I can build anything real inside it, the same best practices reapplied from scratch, every time. That work has a strange status in our industry. We redo it constantly without noticing, and companies never see it at all, right up until a force-majeure incident hits production and it suddenly has to be solved quickly and safely.
To explain why the question is so hard to answer, I have to go back a little.

I started when every platform wanted its own stack
I have more than fifteen years of writing code behind me. I started with C and C++, then moved to Java in the early days of Android, around 2012.
For the work most of us were doing, the web choices felt narrow. The frontend was HTML and CSS with some unsafe JavaScript on top. jQuery, if you wanted help, and almost everyone did. Flash, if you felt brave or had recently made peace with crashing the browser. On the server, PHP was king. That is how WordPress took over so completely that we are still trying to work out how to get rid of it.
Software closer to the machine had its own kingdom. Android meant Java and XML, whether the problem needed XML or not. Apple meant Objective-C and Xcode, whether Xcode was in the mood or not. A company running Windows, which was still most of them, meant the good old .NET, where the solution to one Microsoft dependency was usually another Microsoft dependency.


Android, where XML was the interface whether or not the problem wanted one. 

The iPhone, which also decided what kind of computer you had to own. 

Windows, where shipping meant an installer and a bet on the runtime already present. 

The browser, which asked for the least and initially gave you the least.
None of those platforms was bad for being different. The problem was that every target brought its own language, toolchain, runtime and answer to deployment. A company building for Android, iPhone, Windows and the browser was not shipping one product four times. It was maintaining four related products and hoping they continued to agree.
Fragmentation gave us a word for giving up
That fragmentation produced the landscape we still live in. Most companies have an unmaintained ERP from 2012 somewhere in production, and updating its Windows install feels like migrating a data centre. A Mac that still works perfectly can lose a decade of 32-bit software in one operating-system upgrade.
There is a well-known word for all of this, one we have all heard many times and religiously learned to do nothing about: legacy.
But legacy is not the same thing as local. Some applications live on a user’s machine because nobody has found the budget to replace them. Others live there because that is where their work is: the files, the fonts, the graphics card, the serial port, the hospital network with no route to the internet by design, the note that should open without asking a server on another continent for permission.
A local application is not legacy just because it runs locally.
The real legacy was having to choose between the reach of the web and the capabilities of the machine.
Then the web caught up
Browser support was still a minefield, and standing up a backend meant becoming a part-time sysadmin. Docker had not yet reached its first stable release, and Kubernetes did not exist.
Then Node.js put JavaScript on the server, AngularJS and later React gave us credible application models in the browser, and for the first time a team could write an entire business solution in one language and ship it to almost every device. Better still, the user no longer had to care which runtime the developer had chosen. Nothing to install, no machine-wide dependency to upgrade, no argument about which version of .NET happened to be present. You sent a link. The browser did the rest.
That delivery model was so powerful that we quietly merged two separate ideas: that web technologies had become a universal way to build an interface, and that the software itself belonged on a server. Only the first one had been proven.
The web won as an interface. We mistook that for proof that every application belonged on a server.
We moved everything to the server, then moved half of it back
Everything we used to ship onto the user’s device now lived on a machine somewhere else, a legacy inherited straight from the PHP backend years. So opening a note in your notes app meant several round trips to a server on the other side of the planet. Opening the next note meant doing it again. If the train entered a tunnel, apparently you no longer had notes.
People realised those round trips had to go. Thank God React was here to save us. Client-first applications! Now, to open the same notes app, you waited while what felt like 50 MB of JavaScript downloaded. But at least there was a nice loading spinner to keep you company, and once it landed everything felt instantaneous. Almost native.
The pattern spread quickly, including into critical production software. And as much as I disliked Java and loved React during my younger years, opening my email client became a constant, low-grade frustration. I could go back to the ugly desktop interface, whose designer was apparently sent by Satan himself, or stay on the shiny web client that crashed every five minutes when it was not frozen by a useEffect loop.
We reached the point where we learned to live with buggy software. To normalise it.
We didn’t fix the software. We got better at waiting for it.
Then things genuinely improved. Caching, CDNs, offline storage, local-first databases, optimistic interfaces and edge infrastructure made the web faster and more resilient. But some of that work was treating the symptom: we had taken software away from the machine, discovered that distance and disconnection were problems, then built increasingly sophisticated ways to pretend the software was still nearby.


The desktop did not disappear either. We often rebuilt it with web technologies and shipped an entire browser beside every application: a reasonable bridge, and not a cheap one. The question was no longer whether web technologies belonged on the desktop. It was whether we could bring them back without also bringing back the fragmentation and installation pain the web had removed.
V8 turned JavaScript into infrastructure
None of this would have happened at the same scale without V8. Google shipped the part of Chrome that parses and executes JavaScript separately from the browser APIs around it, and that separation made it embeddable. Node took the engine out of Chrome and put server APIs around it. It later turned up in televisions, embedded devices and other runtimes.
That was the more important breakthrough. JavaScript was no longer tied to a document in a browser: a host could expose files, sockets, processes or hardware, and V8 would handle the language. WebAssembly widened the escape hatch further. JavaScript did not have to do everything. It only had to become the language everything could meet.


So the next problem was never which language should win. It was composing the right ones without recreating the fragmentation we had just escaped.
TypeScript fixed the language, not the discipline
While that was unfolding, something quieter had already taken root.

Fortunately we have living legends like Anders Hejlsberg, the man behind Turbo Pascal, Delphi and C#, who gave us TypeScript.
You can read TypeScript as one more attempt to hide the chaotic, dynamic nature of JavaScript under a sophisticated type system instead of fixing the language’s underlying design flaws. That reading is fair. But the intent was cleverer. By the time TypeScript appeared, the web had already won and was clearly here to stay. Replacing JavaScript was no longer a serious plan; making it survivable at scale was.
Angular carried that idea onto the office desk. Google abandoned AtScript, its own typed superset, and built Angular 2 on TypeScript instead, which handed enterprise teams a frontend that felt like the backends they already maintained: strong typing, dependency injection, and a structure decided for you rather than assembled by you. Java shops had spent years without a credible web story. Angular gave them one their architects could actually sign off on.
TypeScript fixed the language people had already chosen without demanding that they replace it. That detail would matter more than the type system itself.
Angular had the discipline and lost anyway

Angular had already wounded itself before its TypeScript version shipped. At ng-europe in 2014 the team presented Angular 2 as a ground-up rewrite: no $scope, no controllers, no directive definition objects and, as first announced, no migration path for the AngularJS applications sitting in production.
Google walked the worst of it back. ngUpgrade shipped, AngularJS gained a component model, incremental migration became official. Nobody remembers the correction. They remember the announcement: the framework you standardised on can be replaced, and you will find out at a conference.
Then came the learning curve. TypeScript, decorators, dependency injection, modules and RxJS observables, much of it required before you could ship a form. React asked for far less: a library rather than a framework, JSX rather than another template language, and a first screen you could reach in an afternoon. I chose React too, and I chose it for exactly that reason.
React then preserved its component model with unusual restraint. APIs moved at the edges and some legacy features disappeared, but no team was ever told to replace its application in order to keep receiving the framework. The frustrating part is that Angular got better: standalone components made modules optional, signals removed most of the observable ceremony from component state. By then the argument had moved on. Angular had discipline. React had trust.
Freedom is a feature until the codebase gets big
So the industry chose the library without opinions. That freedom is what drew me to React in the first place: compose your own stack, file structure, conventions and libraries. It sounds wonderful on a small indie project and loses its purpose remarkably quickly on a large production application.
You could argue that React simply is not meant for serious software. Unfortunately the truth is very different. Microsoft has shipped React-based interfaces inside Windows and Teams. The absence of architecture did not keep React away from critical systems. It made each team supply the architecture itself.
The problem was never React. The problem is that React has no opinion.
Expo proves that opinions were not the thing developers rejected. It is a mature mobile framework with excellent developer experience, a strong first-party ecosystem and a far more defined way of building, and React Native’s own documentation now recommends starting with a framework such as Expo. Expo did what Angular could not: it added its decisions on top of something people had already chosen, instead of asking them to abandon what they had already built.
Same medicine, and only one of them was swallowed. The industry does not refuse good architecture. It refuses the rewrite that arrives with it.
The same question, one generation later
That is where the web landed. The default way to build software for almost any screen is a library that deliberately holds no opinion about how software should be built, and every team supplies the missing decisions from scratch and calls the result a stack.
Which leaves us facing the question Hejlsberg faced. He could not replace JavaScript; nobody could. So he built a layer on top that made the language safer without asking anyone to abandon the code they had. You do not fix a standard by pretending it lost. You meet it where it already is.
The decade since has produced a wave of projects that understood that lesson, and they are the subject of the next part. The web had become the default. What followed was the work of making that default fast, observable, durable, and able to run wherever the work actually happens.
Continue with How the web stack grew up.
Sources
- 1The first web server at CERN — Coolcaesar, CC BY-SA 3.0commons.wikimedia.org
- 2V8 documentation — Official documentation for the embeddable JavaScript and WebAssembly enginev8.dev
- 3Anders Hejlsberg at PDC 2008 — DBegley, CC BY 2.0commons.wikimedia.org
- 4Angular 2.0 Core session at ng-europe 2014 — Conference recordingyoutube.com