The Last Application · Part I — The Weight of Code

Chapter 1 — A Brief History of the Application

14 min read

Some years ago, a large American bank went looking for someone who could read a program written before most of its employees were born. The program calculated interest accruals. It had been written in COBOL in 1974, it had run every night since, and nobody left in the building could say precisely what it did. There was documentation — a binder, someone remembered — though the binder had not been seen since an office move in the nineties. Turning the program off was out of the question; it was load-bearing in ways no one could enumerate, which is the most dangerous kind of load-bearing there is. The story reached me secondhand, the way everyone in this industry eventually receives a version of it — the details vary with the teller; the shape never does. By the most commonly cited estimates, a couple hundred billion lines of COBOL remain in production today, and a substantial share of the world's daily financial transactions pass through code whose authors have retired and, in a growing number of cases, died. The machines have kept every promise. The humans kept none of the knowledge.

I begin with that scene because it is the destination of this chapter, not a curiosity along the way. Everything the software industry has built for seventy years converges on it: an application that works, that matters, and that has become unreadable to the institution that owns it. This outcome is not bad luck, and it is not bad engineering — the accrual program was probably excellent engineering. It is the normal fate of the artifact. To see why, we need to walk the history of the application itself. It is a shorter walk than you might expect, because beneath all the churn of languages and frameworks, the artifact has changed shape only twice — and it is straining to change a third time right now.

Three generations

The first generation of enterprise applications was pages. From the green-screen terminals of the 1970s, through the client-server boom of the late eighties and nineties — Visual Basic, PowerBuilder, Oracle Forms — to the web applications that replaced them all after 1995, the fundamental unit of the application was the screen. You put a form in front of a user; the form read and wrote a database; the sum of the forms was the product. The industry even had a dismissive shorthand for it — "forms over data" — dismissive because everyone sensed that a stack of screens was not really a model of a business, merely a set of windows onto its records. But it shipped, and for two decades it was what "an application" meant.

The second generation was workflows. Sometime around the turn of the millennium, the industry noticed that an enterprise is not a pile of records but a set of processes — orders that move from placed to fulfilled, claims that move from filed to settled — and that the pages were just the places where humans touched the process. So the process itself became the product. Business Process Management suites promised to let analysts draw the process and have it execute. Standards bloomed: BPEL for orchestrating services, BPMN for diagramming the flow. Service-oriented architecture rebuilt the back end as callable capabilities, and software-as-a-service — Salesforce, founded in 1999, soon built its brand on the promise of "the end of software" — moved the whole apparatus off your premises and onto someone else's. The unit of the application was no longer the screen. It was the flow.

It is worth noticing how each generation marketed itself, because the pitch never changes. Client-server was sold as liberation from the mainframe priesthood. The web was sold as liberation from the desktop deployment nightmare. BPM was sold as liberation from the programmers themselves — draw the process, press run. SaaS was sold as liberation from all of it at once. Every generation promised that this time the business would finally hold its own description, and every generation delivered the description into a new form of code — Java instead of COBOL, process definitions instead of screens, someone else's data center instead of yours — where it promptly became the next thing to be liberated from.

The third generation is being sold to you at this very moment: agents. The unit is no longer the screen or the flow but the goal. You state an outcome; a machine intelligence plans the steps, calls the tools, and reports back. Every vendor keynote of the past two years has featured one. I will have a great deal to say about this generation in the next chapter, most of it uncomfortable, so for now I will note only the pattern that connects all three.

Each generation did not upgrade the previous one. It replaced it. The client-server systems were not migrated to the web; they were rewritten for the web, screen by screen, over years, at enormous cost. The page-era products were not extended with workflow; companies bought workflow suites and rebuilt around them. This is worth pausing on, because it is strange. A building gains plumbing and then electricity and then networking without being demolished each time. An application, confronted with a new paradigm, gets demolished. The reason is the artifact itself — and to understand the artifact, we have to talk about what a program actually is.

Why code rots

Here is the standard story of software: a programmer describes the desired behavior in a programming language, a compiler translates the description into something a machine executes, and the description — the source code — remains behind as the authoritative record of what the system does. Change the description, recompile, and the system changes. The description is the product. It is a tidy story, and nearly everyone in the industry professes to believe it.

The trouble is that it has been known to be false for forty years. In 1985, the Danish computer scientist Peter Naur — the N in BNF, a man with some standing on the question of what programs are — published a short paper called "Programming as Theory Building." Naur's argument was that the essential product of programming is not the program text at all. It is a theory, in the working-knowledge sense: the understanding, held in the heads of the programmers, of how the text maps onto the world — why this module exists, what that invariant protects, which parts are deliberate and which are scar tissue. The text records the decisions. The theory holds the reasons, and the reasons are what you need in order to change the system without breaking it. Naur's brutal conclusion: the theory cannot be written down. Not in comments, not in documentation, not in the code itself. It lives in people, it is transmitted only by working together, and when the people leave, it dies. A program whose theory has died is not merely under-documented, in Naur's account — it is dead, and its continued modification by people who lack the theory produces exactly the degradation every maintenance programmer knows on sight: patches that fight the design, special cases that multiply, a system that becomes stranger and more brittle with every fix.

This is the description/behavior gap, and it is the engine of everything this chapter describes. The code says what the machine does, in perfect and useless detail. What the product is — what the business meant by it — was never in the code. It was in the theory. And theory evaporates on a timescale of years, because people leave on a timescale of years, while applications live on a timescale of decades. My bank's accrual program was not missing documentation. It was missing its people, and no binder was ever going to substitute for them, which may be why nobody looked very hard for the binder.

Notice that this diagnosis has nothing to do with code quality. Well-factored code with beautiful tests loses its theory too — a little slower, that is all. Nor does it have to do with documentation discipline; Naur's point is precisely that the theory is the kind of knowledge that resists being written, the way knowing how to ride a bicycle resists being written. The rot is not in the text. It is in the arrangement: the authoritative description of the product is written in a form that only a small set of humans can interpret, and the interpretation itself is stored nowhere at all. Any arrangement with that shape decays. Ours has had that shape since FORTRAN.

The rewrite ritual

Every civilization develops rituals for managing what it cannot prevent, and the software industry's ritual for theory-death is the rewrite.

The ceremony is remarkably standardized. An application ages past the point where anyone can change it confidently. Estimates for small features stretch from days to quarters. Engineers begin using the word "legacy," which in software means the code works and we are afraid of it. Eventually leadership convenes and decides that the responsible course is to rebuild the system on a modern stack. A budget is approved — eight figures is customary for anything that matters — and a timeline of eighteen months is announced, which everyone privately doubles.

Then comes the part that would be comic if it were not so expensive: the team discovers that nobody can specify what the old system does. The code is the only complete description of the product, the code has stopped being readable as a description, and so the rewrite team ends up doing archaeology — reading decompiled behavior, interviewing veterans, black-box-testing their own product — to recover requirements their own company wrote down once, in code, and then lost. The old system becomes the specification of the new one, which guarantees that its accidents are faithfully reproduced along with its intentions.

The economics deserve a moment of respect, because they are genuinely strange. During the rewrite — typically two to four years for a system of consequence — the company pays twice: once to keep the old system alive, once to build its successor, while the feature roadmap idles because every new capability would have to be built in both. The business case that justified all this was almost never "the old system does the wrong thing." It was "we can no longer change the old system safely" — which is to say, enterprises pay eight figures not for new behavior but for restored legibility, and they pay it in the only currency the paradigm accepts: starting over.

Fred Brooks named the ritual's other failure mode in 1975: the second-system effect, the tendency of designers, freed at last from an old system's constraints, to load the replacement with every idea they deferred the first time. The rewrite arrives late, over budget, more elaborate than its predecessor — and made of exactly the same material. That is the detail the ceremony never confronts. The new system is code. Its theory begins evaporating the day it ships. The company has not cured the disease; it has purchased, at eight figures, a younger patient. Fifteen years later the ritual repeats, and it is genuinely repeated — there are enterprises now on their third or fourth rebuild of the same essential product, each rebuild justified by the unreadability of the last.

An industry does not sustain a ritual this costly without trying to escape it. The escapes deserve their own section, because their failures are more instructive than most successes.

The escape attempts

CASE tools. In the 1980s, Computer-Aided Software Engineering promised to move development up a level: analysts would draw data models and flow diagrams in tools like Excelerator and Texas Instruments' IEF, and the tool would generate the code. For a while this was a billion-dollar market. It collapsed on a single, humble problem: round-tripping. The generated code was never quite sufficient, so programmers edited it by hand — and the moment they did, the diagrams upstream were wrong. The tools could not reliably absorb hand edits back into the model, so teams faced a daily choice between the diagram and the deadline. The deadline won every time. Within a few years the diagrams were decoration, then shelfware.

4GLs. The fourth-generation languages of the same era — FOCUS, RAMIS, Natural, and their kin — promised applications without programmers; James Martin's 1981 book was titled, with admirable directness, Application Development Without Programmers. What actually happened is that the easy eighty percent of an application got easier and the hard twenty percent got harder, because the language had traded generality away. Every 4GL shop ended up with a priesthood of 4GL specialists — programmers by any other name, now hostage to a single vendor's runtime. The description had moved to a new language. It was still code, still opaque to the business, and now also proprietary.

UML and Model-Driven Architecture. The 2000s replayed CASE with better theory. UML unified the modeling notations in 1997; the OMG's Model-Driven Architecture, launched in 2001, proposed that platform-independent models would be the real source, transformed mechanically into platform-specific code. The vision was coherent and the failure was the same one, wearing a nicer suit: the code remained where the behavior lived, so the code remained where the changes happened, so the models drifted. A model that must be manually reconciled with a faster-moving artifact is not a source of truth; it is a summary with pretensions. Ten years in, most UML diagrams in the wild were drawn after the code they described, for the benefit of auditors.

Low-code. The 2010s attacked the aperture problem from the demand side: too few programmers, so let power users assemble applications visually. Forrester coined "low-code" in 2014, and the platforms — OutSystems, Mendix, Appian, Power Apps — genuinely work for a real class of application. But look at their anatomy and you find something telling: a form builder holding the pages, a workflow engine holding the processes, a rules engine holding the logic — three tools, three stores, three half-descriptions of one product, wired together with glue that is code by another name. Low-code did not unify the description of the application. It fragmented it, and each fragment was locked in a proprietary format, which is why the industry that promised the end of legacy code has already produced legacy low-code.

RPA. Robotic Process Automation, the boom of the late 2010s, deserves respect for its honesty. Confronted with decades of enterprise systems whose logic was unreachable — no APIs, no readable source, vendors long absorbed or defunct — RPA gave up on reaching the meaning at all and automated the pixels: software robots clicking through the same screens as the humans, reading values off the glass. It worked, fragilely, until a form field moved. As a business, RPA was a triumph. As an engineering statement, it was a confession: the machine's meaning is so thoroughly buried that the cheapest way to reach it is to impersonate a clerk. An industry does not screen-scrape its own applications because things are going well.

The pattern

Line the failures up and they stop looking like separate stories. CASE drew the model beside the code. MDA drew a better model beside the code. Low-code split the model into three tools beside each other. RPA gave up on the model and read the screens. In every case, one thing was never touched: the code remained the source of truth. Every escape attempt added a second description of the product — a diagram, a model, a visual flow — and left the first description in charge.

And second descriptions always lose. This is close to a law of nature in software, and the mechanism is mundane: when the deadline arrives, the change is made wherever the change takes effect — in the authoritative artifact — and the secondary description is updated later, which is to say, at first sporadically and then never. Documentation loses to code this way. Diagrams lose to code this way. Models, wikis, runbooks, and architecture decks all lose to code this way, and no discipline, tooling, or managerial exhortation has ever stopped it for long, because the incentive gradient renews itself with every single change. The only description that stays true is the one the system actually runs.

Which points, once you see it, at the only exit the pattern permits. The escape is not a better second description. It is the promotion of the description: the definition of the product — its pages, its processes, its policies — must itself become the thing the system executes, so that there is exactly one description and it cannot drift from the behavior, because it is the behavior. Every failed escape of the past fifty years was an approximation of this idea, built before the one ingredient existed that could make it worth the cost: a reader — something other than scarce human programmers that could interpret a rich, structured self-description and act on it. The rest of this book is about what happens now that the reader has arrived.

The point

An application is a description of a business, written in a language the business cannot read, whose real meaning lives in the heads of its authors and evaporates as they leave. That is why code rots — the text persists while the theory dies — and why the industry's recurring answer, the rewrite, only resets the clock on the same decay. Fifty years of escape attempts — CASE, 4GLs, UML, MDA, low-code, RPA — failed for one shared reason: each added a second description of the product while leaving code as the source of truth, and second descriptions always lose. The only durable exit is to make the description itself executable and authoritative — one description, governed, that the system runs. What was missing was something that could read it.