Wirth’s Law states that software is getting slower more quickly than hardware is getting faster. It’s held up embarrassingly well since Niklaus Wirth named it in 1995 — chat apps that need a gigabyte of RAM to display text, IDEs that take longer to open on a modern SSD than they took on a spinning disk a decade ago, “lightweight” tools that bundle an entire browser engine to render a settings page.

It’s usually presented as an inevitability, a kind of software-side entropy. It isn’t. It’s a description of what happens when every release recompiles the whole iceberg instead of the one piece that actually changed.


The Iceberg-Recompile Tax

Borg’s confidence — we will just recompile the iceberg every night — works inside a monorepo where every caller is visible and updated atomically in the same commit. Apply the same instinct to consumer software shipped to strangers, and the atomic-rebuild-everything habit stops being efficient and starts being the tax Wirth’s Law measures.

Electron apps are the clearest everyday example — bundling a complete, prebuilt Chromium and Node.js runtime into every single app, redundantly, rather than resolving against a shared copy already on the machine. More on exactly how that differs from the JVM’s failure mode further down. Hardware gets faster. The tax scales with how much gets duplicated on every install, and nobody bounded that number, so it keeps growing to match whatever disk and bandwidth the hardware can absorb.

This is the mechanism, not a metaphor: Wirth’s Law measures the rate at which unbounded iceberg-dragging consumes hardware gains. It isn’t a property of software in general. It’s a property of software that never separated the part that changes from the part that doesn’t.


Android’s Layers Are Not Allowed to Touch

Android beats Wirth’s Law, not through some clever optimisation trick, but by structurally refusing to let an app carry the OS along with it.

The runtime is shared and stays put. ART, the Android Runtime, sits underneath every app on the device — one shared, system-maintained layer that individual apps never bundle a copy of. An app doesn’t ship its own JavaScript engine or its own garbage collector the way a bundled-runtime desktop app ships its own browser engine wholesale. It resolves against whatever ART is already installed. This is the file-boundary discipline from the HTML/CSS/JS post applied at OS scale: the runtime is its own file, its own layer, and applications don’t get to reach in and replace it with their own copy.

Play Services is a second shared layer, updated independently of both the OS and individual apps. Location, push notifications, in-app billing, dozens of common services that would otherwise be bundled per-app live in one place, updated on Google’s schedule, resolved against by every app that needs them. An app doesn’t recompile Play Services into itself. It calls it, the way a program calls a system library instead of statically linking a copy.

Individual APKs update as sealed, independent units. Updating one app never touches another app’s code, never touches the OS, never touches the runtime underneath it. The Play Store ships delta updates — only the bytes that actually changed between versions, not the whole package again — because the resolver (the Play Store’s update mechanism) knows precisely which piece of the iceberg moved and ships exactly that piece.

None of this is optional cleverness bolted on afterward. It’s the consequence of drawing the seams once, at the OS/SDK/app boundary, and refusing to let any layer casually reach across it — the same discipline the series has been arguing for since the modularity post: the Use (the app) can be replaced, updated, or removed without dragging the Def (the runtime, the OS, Play Services) along for the ride, and vice versa.


The Proof Is the Old Phone That Still Works

A five-year-old Android phone runs current apps because most of what makes those apps “current” lives in layers the phone shares with every other device running the same OS version, not in weight each individual app has to carry alone. The app itself can stay small because it isn’t hauling its own runtime, its own service layer, its own copy of everything it depends on. Updating it means shipping a delta, not a re-ship of the iceberg.

Compare an old laptop straining under a stack of desktop apps that each bundle their own complete runtime, none of them aware the other forty apps on the machine are carrying an identical prebuilt payload. That laptop is storing and loading forty redundant copies of the same base layer. The phone is storing one shared base layer that forty apps take turns standing on.

Wirth’s Law isn’t wrong about what actually happens to most software. It’s just not describing software. It’s describing the absence of a resolver — what happens by default when nobody drew the seam between “the part that’s shared” and “the part that’s yours,” and every release quietly recompiles everything rather than the one thing that changed. Draw that seam properly, the way Android did at the OS boundary, and the law stops applying, not because the hardware got faster, but because the tax it was measuring was never collected in the first place.

Borrowed Foundations, Evolved Independently

None of this discipline was built from scratch, which is itself the point. Android took what already worked and left the rest exactly the way the Linux desktop post described — the kernel is the real Billy, indifferent to what’s stacked on it, and Android kept that indifference while discarding the tribal infighting that makes the Linux desktop so unpredictable.

The kernel is stock Linux, patched but recognisably the same project every server on the internet runs. Android didn’t reinvent process isolation, memory management, or scheduling — it resolved against an existing, battle-tested Def rather than building a new one to prove a point.

Where Android did build its own, it built for a specific reason rather than out of NIH reflex. Bionic, Android’s own C library, exists instead of glibc because glibc’s licensing (LGPL) creates obligations that don’t fit cleanly into a system shipping proprietary vendor code alongside open source — Bionic is BSD-licensed, smaller, and built for the specific constraint of a mobile device rather than a general-purpose workstation. That’s NIH used correctly: not “we can build it better,” but “the existing thing has a real constraint that doesn’t fit our use case, so we built the minimum needed to solve exactly that constraint.”

The early runtime borrowed from Java the same way — Dalvik used Java-compatible bytecode (compiled from .class files down to .dex), letting the entire existing pool of Java developers and their tooling carry over into Android with minimal relearning, without Android ever needing to own or ship a full JVM. ART, Dalvik’s successor, kept the same compatibility contract while changing everything about how compilation actually happens underneath it. The Semantic layer developers write against — the API surface, the bytecode target — stayed stable. The Gutenberg layer running it was free to be replaced entirely, twice, without a single app needing to be rewritten.

Project Mainline is this same discipline applied inside the OS itself, years after Android shipped. Instead of one monolithic OS image where updating anything means updating everything, Mainline splits core system components — media codecs, security providers, networking components — into independent modules, each updatable through the Play Store on its own schedule, without a full OS update and without every device manufacturer needing to ship a new build. It’s the Java-modularity argument turned inward: even Google’s own OS stopped treating itself as one atomic iceberg and started resolving its own internal pieces independently, the same lesson Borg never had to learn because Borg’s callers were never strangers.


What Actually Happens When You Buy a New Phone

The clearest proof this discipline works end to end is the ordinary experience of upgrading hardware.

Your content transfers — photos, messages, accounts, settings — because none of that was ever coupled to the specific silicon of the old device. It’s Semantic content, portable by design. What happens next is where the resolver does the interesting work: the Play Store doesn’t just copy the old APKs across. It re-downloads and recompiles each app, freshly, optimised for the new phone’s specific CPU architecture and GPU. ART’s ahead-of-time compilation is device-specific — the same app resolves to different compiled machine code depending on exactly what chip it’s running on, the same way a CDN resolves the same hostname to a different physical server depending on who’s asking. The app’s identity never changes. Its Gutenberg-layer optimisation is re-resolved, silently, every time the hardware underneath it changes.

None of this works without API-level stability as the actual contract underwriting it. Every Android app declares a target API level and a minimum API level — a promise about which version of the Semantic contract it was built against and how far back it’s willing to run. That contract is what lets a five-year-old app still install on a brand-new phone, and what lets a phone running last year’s OS still run apps built against this year’s SDK, within bounds. It’s the same role the @ sign plays for email or a semver range plays for a package — a stable name that both sides agree to resolve against, so that neither side has to freeze in place for the other to keep working. Buying a new phone doesn’t mean starting over, because nothing about the transaction ever depended on the hardware staying the same. Only the API contract had to.

Log Cabin, Walled Garden, or Real-World Use

There turn out to be three distinct failure modes hiding under Wirth’s Law, not two, and it’s worth naming all three plainly: the log cabin, the walled garden, and real-world use. The log cabin carries an entire world with it, whether by reinventing it from scratch or by bundling a redundant prebuilt copy. The walled garden builds real internal modularity and then never solves how to resolve against anything outside its own walls. Real-world use resolves thinly against whatever substrate is already standing, borrows what already works, and reserves its own engineering for the part that’s actually novel.

The log cabin has two variants, and both waste the hardware gain the same way. The full JVM and the full .NET runtime are the log cabin built from raw material: their own bytecode format, their own garbage collector, their own threading abstractions, their own sprawling standard library, substantially reimplementing functionality the OS underneath already offers, in the name of “write once, run anywhere.” That’s an enormous, ongoing engineering investment to maintain a parallel world, and the cost doesn’t shrink just because the hardware underneath it got faster. Electron is the log cabin’s other variant — not reinventing a world, but mechanically importing an identical prebuilt one, per app, redundantly. It links a complete Chromium and Node.js binary into every single package rather than resolving against a shared copy already on the machine. A bloated Docker image repeated across every container instead of one shared base layer everyone builds from is the same failure in the server room. Reinvented or reduplicated, the shape is identical: you’re carrying your own civilisation instead of standing on the one already built.

The walled garden is Pascal, Modula, and Ada — real modularity, no external resolution. Wirth named the law and spent his career building an alternative to it: Pascal, then Modula-2 — literally named for modules, a genuine working answer to the log cabin, with real compilation units, real interfaces separated from implementation, real independently-buildable pieces. He built the resolver-based alternative before most of the industry took the problem seriously. It didn’t stop the log cabin from becoming the industry default anyway, which is presumably not what he’d hoped for, though that’s an inference about intent rather than something the record actually states.

The reason it didn’t stick has less to do with the language’s own merits and more to do with what sat next to it. Unix was written in C, so C had zero-friction, native access to every system library, every OS call, the entire substrate. Pascal calling into a C library always needed a bridging layer, a calling-convention translation, an FFI boundary C code never had to cross because it was already standing on the same ground as the thing it was calling. The internal modularity could be excellent — and by most accounts it was — and it still didn’t matter for adoption, because the wall around the whole language was itself a resolver failure. Modular on the inside, permanently a guest on the outside.

Ada deserves real credit here rather than a flat dismissal: designed for exactly the domains where a walled garden’s tradeoffs make sense — avionics, rail signalling, defence systems — where being cut off from the fast-moving, informally-verified C/Unix ecosystem is a feature, not a bug, and where Ada’s strict typing and formal verification tooling have a genuine, decades-long safety record (DO-178C-certified avionics software still ships in Ada today). The critique isn’t that Ada failed at its actual job. It’s that the same walled-garden shape — real internal modularity, genuine safety guarantees, comprehensive scope, no cheap resolution against the wider ecosystem — makes it a poor fit for anything outside that narrow, safety-critical niche, which is a much smaller claim than “Ada was a mistake.”

Rust is closer to what a walled garden looks like when it solves the external-resolution problem too. It offers memory safety guarantees in the same spirit Ada does, without a garbage collector, and pairs that with cargo — a real resolver, not a committee specification, connecting a short semantic crate name to a verified, hash-checked artifact the way npm and Maven eventually learned to do and Ada’s own tooling never quite prioritised. Rust also has a working, if imperfect, FFI story for calling into and being called from C, which is why it’s been accepted into the Linux kernel itself for driver code in recent years — the walled garden finally negotiating a real gate in its own wall, into the one ecosystem Pascal could never get past. It hasn’t solved everything Ada was built to solve — the certification tooling and decades of aerospace track record aren’t something a younger language matches overnight — but it’s the clearer proof that safety and external resolution aren’t actually in tension. Someone just had to build both at once.

The proof that a walled garden could also be lean arrived from someone else entirely, decades before Rust. Anders Hejlsberg’s Turbo Pascal, built on Wirth’s own language, shipped a complete single-pass compiler, editor, and linker that fit in around 39 kilobytes and ran an entire edit-compile-run cycle in seconds on hardware today’s tooling wouldn’t tolerate running a linter on. It was commercially dominant for years, genuinely lean, genuinely fast — and still walled off from the C/Unix substrate everyone else was standing on. By the time it had proven the internal discipline was possible, the industry had already committed to C and Unix’s own layering, not because that layering was more elegant, but because Unix and C already had the ecosystem, the installed base, the momentum. Worse ages better than perfect, once again: the working system that was already spreading beat the better-designed one that arrived second, wall or no wall.

Real-world use is Android, and eventually Hejlsberg, further down the same road. ART sits outside both the log cabin and the walled garden, deliberately. It borrowed the Semantic idea from Java — bytecode compatibility, so the entire existing pool of developers and their tooling carried over with minimal relearning — without importing the comprehensive log-cabin JVM wholesale, and without shipping a redundant copy of that runtime per app the way Electron ships Chromium. One shared, purpose-built runtime underneath every app, resolved against rather than reinvented, reduplicated, or walled off.

The Linux kernel makes the same choice at the language level, and it’s worth framing carefully rather than as a verdict. Linus Torvalds has kept C++ out of the kernel for decades, and his stated reasoning is closer to refusing Borg’s recompile-the-universe stance than a technical takedown of C++ itself: abstractions like operator overloading and implicit allocation hide cost at exactly the layer where a kernel needs to see cost plainly, line by line, against real hardware. That’s a defensible engineering position, not an objectively settled one — plenty of serious systems software is built in C++ successfully, and the kernel’s stance has drawn real pushback as much as agreement over the years. What it shares with the rest of this taxonomy is the same instinct: less is more, worse ages better than perfect, prefer the smaller thing that resolves cleanly against what’s already there over the more capable thing that hides what it’s actually doing.

Hejlsberg’s trajectory across three languages reads less like a fix and more like an engineer who learned and moved on as the ground underneath him changed. Turbo Pascal chose speed and a small footprint, on hardware where that was the whole game. Delphi chose pragmatic Windows interop, because Windows was where the users were. TypeScript chose compiling directly into JavaScript’s actual, messy, already-dominant ecosystem, because by then the web was where the users were. None of the earlier choices were wrong for their moment — Turbo Pascal’s walls didn’t matter as much when DOS was the entire world worth resolving against. What changed was the substrate itself, and Hejlsberg kept building for whichever one was actually there rather than defending the one he’d already built for.

TypeScript’s actual mechanism is worth spelling out, since it’s the clean technical version of that same instinct: it compiles directly into JavaScript — the substrate itself — inheriting every existing library, every npm package, the entire browser ecosystem, natively, with no FFI layer and no translation boundary at all. Where Turbo Pascal proved lean compilation could work inside its own walls, TypeScript layers cleanly on top of the fast-moving substrate JavaScript had already become, resolving against it directly instead of requiring anyone to bridge in. Real-world use, three decades and one language ecosystem later, from the same person, having learned where the ground had moved to.

The same instinct showed up again in 2025, and it’s recent enough to still be unfolding: Microsoft ported the TypeScript compiler itself to Go, not Rust. Rust was the expected choice — the systems language with the safety story this post has already credited — but Hejlsberg’s own stated reasoning for picking Go instead was entirely pragmatic: the port could stay structurally close to the existing codebase rather than requiring a rewrite, Go’s garbage collector is simply fine for a batch compiler that isn’t running inside anything real-time or safety-critical, and the team already knew Go well. No theoretical purity argument won. The tool that resolved most directly against the team’s actual existing code and actual existing skills won, the same way it did in 1983 and the same way it did with TypeScript itself in 2012.


The Flatpack House

The Billy bookcase from the earlier post was the first flatpack in this series, and Android is the fully furnished house built entirely from flatpacks — assembled from shared, catalogue parts rather than either hand-built from raw material or shipped in as redundant whole containers. The kernel is a wall panel manufactured once, at scale, by people who specialise in walls, delivered ready to bolt on. Bionic is the plumbing kit, purpose-built for a smaller footprint than the standard one because the standard one doesn’t fit this particular house. ART is the wiring system — a lean, purpose-built installation rather than the log cabin’s own private power station — upgradeable behind the walls without anyone needing to re-plaster. Mainline is the modular kitchen units, swappable one at a time without gutting the house. Buying a new phone is moving into a new flatpack house built from the same catalogue, where your furniture — your content — just gets carried over, because none of it was ever nailed to the specific walls of the old place.

Wirth’s Law measures how much of the hardware gain gets consumed by the log cabin, the walled garden, or the gap between them. Hardware got faster; none of these three got any smaller as a result. The only way the arithmetic ever improves is real-world use — resolving against shared parts instead of reinventing or reduplicating or walling them off — which is exactly the decision Android’s architecture made, one layer at a time, starting with the one wall panel — the kernel — that never needed reinventing at all, and finishing with a runtime that did what Modula-2 promised in 1978 and mostly delivered, this time.


This post is part of the Gutenberg/Semantic series. Related: The Billy With Opinions on Android taking the Linux kernel and leaving the tribal infighting, Borg’s Arrogance on the cost of recompiling everything atomically, and com.google.gson Goes Nowhere on modularity as replaceability.