Vivian Voss

The Update Treadmill

javascript architecture web

Beta Stories ■ Episode 6

Your software worked on Friday. You ran npm update on Monday. It no longer works on Monday. Nothing in your code changed. Not a single line. Not a character. Not a whitespace alteration that an overzealous linter might have introduced. The platform moved underneath you, like a carpet being pulled by someone who felt the room needed rearranging.

One does find it rather charming that the industry calls this "progress."

The Promise

"Ship early, iterate fast, embrace change." Agile taught an entire generation that stability is something to overcome. Sprints replaced milestones. Continuous deployment replaced releases. The word "done" quietly left the building and has not been seen since. Nobody filed a missing persons report, because filing a report would require a finished form, and the form is in the next sprint.

The ecosystem took the hint. Angular ships a new major version every six months. Eighteen major versions in ten years. Each with breaking changes. Each with an eighteen-month support window. Each politely informing you that the version you spent three months migrating to is now approaching end of life. You are not building software. You are servicing a subscription to your own framework.

The build tools followed suit. Grunt was the standard in 2013. Gulp replaced it by 2015. Webpack took over by 2017. Now it is Vite, with Webpack satisfaction down to 26 per cent. Four generations of tooling in ten years, each requiring its own configuration language, its own plugin ecosystem, its own mental model. The code you built did not change. The machinery around it was replaced four times. One does rather admire the confidence required to call this an improvement.

Build Tool Churn: Four Generations in Ten Years 2013Gruntreplaced 2015Gulpreplaced 2017Webpack26% satisfaction 2023Vitecurrent Your code did not change. The machinery around it was replaced four times. Each required its own configuration language, its own plugins, its own mental model. Angular: 18 Major Versions in 10 Years Each with breaking changes. Each with an 18-month support window. You are servicing a subscription to your framework.

The Decay

44 per cent of breaking changes in npm packages arrive in minor and patch releases. The versions that semantic versioning promises are safe. They are not. They merely look safe, which is rather worse, because you install them without reading the changelog, and the changelog would not have helped anyway because the breaking change is in a transitive dependency three levels deep that you have never heard of.

Next.js 15 reversed its caching defaults. Code that relied on cached responses now fetches on every request. Performance regressions delivered free of charge, disguised as a feature. Next.js 16 dropped Node.js 18 entirely. One does not recall requesting either of these changes, yet here one is, updating CI pipelines on a Tuesday afternoon.

The migrations that do get announced fare little better. Python 2 to 3 took twelve years. Twelve years for a language migration that was meant to be straightforward. The cost to the industry has been estimated at over $100 billion. AngularJS reached end of life in 2021. Four years later, 1.2 million websites still run it, with 419,000 weekly npm downloads. The migration was not completed. It was abandoned. The websites are still there, running a framework that nobody supports, serving users who do not know and would not care.

Where Developer Time Goes 75% maintenance not features, not innovation 33% technical debt Stripe, 2018 13.5h per week per dev on inefficiencies 44% of npm breaking changes in minor/patch 200 Dependabot PRs/week (one monorepo) 75% of engineering talent spent keeping the treadmill running so that one may continue running on the treadmill. Splendid.

Stripe measured the damage in 2018: developers spend 33 per cent of their time on technical debt. 13.5 hours per week per developer on inefficiencies that produce no value. Across the industry, 75 per cent of development time goes to maintenance. Not features. Not innovation. Keeping the treadmill running so that one may continue running on the treadmill. Splendid use of engineering talent.

The Mechanism

Framework authors need adoption. Adoption requires novelty. Novelty requires breaking the previous version. Break, migrate, break, migrate. The cycle is not accidental. It is the product. The framework is not a tool. It is a subscription that bills in engineering hours instead of pounds.

Agile provided the philosophy. "Embrace change" became "inflict change." Sprints guarantee a next iteration, never a stable release. The process ensures that software never reaches a finished state, and the vocabulary ensures nobody notices: it is not instability, it is iteration. It is not churn, it is evolution. It is not a bug, it is a known issue in the backlog of the sprint after the sprint after this one.

And the tooling enforces it. Dependabot opens 200 pull requests per week for a monorepo. Not because your code is broken. Because a dependency four levels deep released a patch for a vulnerability in a function you do not call. The automated pull request arrives, the CI runs, the reviewer spends twenty minutes verifying that nothing changed, and the treadmill advances one step. Multiply by fifty projects and you have a full-time job that produces nothing.

The Signal

SQLite has maintained backwards compatibility since 2004. The developers have committed to preserving the current file format, SQL syntax, and C API until the year 2050. That is not a version policy. That is a promise to every system that depends on it. Twenty-two years of stability so far. Twenty-four more to go. One does rather appreciate a roadmap that fits on a single sentence.

POSIX has been stable since 1988. Thirty-eight years of API consistency across operating systems, vendors, and hardware architectures. A shell script written in 1990 still runs. A C programme compiled against the POSIX API in 1995 still compiles. The specification was not abandoned. It was finished. The industry has apparently forgotten that this is possible.

FreeBSD has maintained source compatibility across major releases for over thirty years. Code written for FreeBSD in the 1990s compiles and runs on FreeBSD 14 today. Not because nobody touched the system. Because every change was made with the understanding that other people's software depends on it.

Stability: A Design Decision Angular10 years18 breaking versions SQLite22 years0 breaking changes POSIX38 years FreeBSD30+ years These are not legacy projects. They are proof that stability is a design decision. The treadmill is not inevitable. It is profitable.

These are not legacy projects. They are proof that stability is a design decision, not a technical limitation. The treadmill is not inevitable. It is profitable.

Your framework updates every six months. Your database file format has not changed in twenty-two years. One of them understood what "production" means.

"Go into frameworks," they said. They did not mention the frame gets replaced every six months whilst you are still hanging in it.

Angular: 18 major versions in 10 years. 44% of npm breaking changes in minor/patch releases. 75% of dev time on maintenance. Python 2 to 3: 12 years, $100B+. SQLite: backwards-compatible since 2004. POSIX: stable since 1988. The treadmill is not inevitable. It is profitable.