· 6 min read ·

Good Ideas Take Decades: The Adoption Lag Behind Software's Best Innovations

Source: lobsters

The Inventor Is Rarely the One Who Wins

David Wheeler, who wrote one of the most careful surveys of important software innovations, is himself an example of the problem he documents. Wheeler implemented the first reusable subroutine library on the EDSAC in 1949. Subroutines are now so foundational that programmers do not think of them as an invention at all. That invisibility is partly the point: the ideas that win completely tend to disappear into the background, which makes it easy to forget that someone had to fight for them.

What Wheeler’s survey reveals, if you read it with adoption timing in mind rather than just invention dates, is a consistent and uncomfortable pattern. The gap between “first working implementation” and “mainstream practice” is rarely a few years. It is commonly a decade, and often two or three. The question worth sitting with is not which innovations mattered, but why smart, working programmers took so long to use them.

Garbage Collection: Thirty-Five Years in the Waiting Room

John McCarthy implemented mark-and-sweep garbage collection for LISP around 1959-1960. The algorithm is conceptually simple: trace all reachable objects from a set of roots, mark them, then sweep through memory and reclaim everything unmarked. It worked. It shipped in a real language. Programmers used it.

For the next three and a half decades, the mainstream largely ignored it.

The objections were not irrational. Early GC implementations were slow and had unpredictable pauses. Manual memory management, for all its footguns, gave programmers control and predictability that GC could not match on the hardware of the 1960s and 1970s. C arrived in 1972 and codified manual allocation as the serious programmer’s approach. The culture around systems work treated GC as a toy for academic languages, not production code.

The algorithmic work continued despite the indifference. David Ungar’s 1984 paper “Generation Scavenging” introduced generational collection, exploiting the observation that most objects die young. This made GC substantially faster and reduced pause times. The research community understood the significance. The industry took another decade to care.

Java arrived in 1995 with garbage collection built in and no opt-out. That was the moment. Not because Java’s GC was technically superior to everything before it, but because Java forced the question. Millions of programmers who had never thought carefully about memory management now had to reckon with a language that handled it for them, and the world did not end. Productivity went up. A generation of programmers grew up treating GC as normal.

The subsequent decades have been a long refinement of what McCarthy started. Go uses a tricolor mark-and-sweep collector with concurrent marking. The JVM now ships ZGC and Shenandoah, both designed for sub-millisecond pause times even on multi-terabyte heaps. Rust took a different path entirely, using a borrow checker to enforce memory safety statically at compile time, eliminating the need for a runtime collector while preserving safety guarantees, though at the cost of a steeper learning curve.

Thirty-five years from invention to mainstream. The idea was right in 1960.

Structured Programming: A Decade of Argument Over Something Obvious in Retrospect

The Böhm-Jacopini theorem, published in 1966, proved that any computable function could be expressed using only three control structures: sequence, selection, and iteration. You do not need goto. The mathematics was clear.

Edsger Dijkstra wrote his famous letter “Go To Statement Considered Harmful” to the Communications of the ACM in 1968. It is one of the most cited papers in computer science and one of the most argued-about. Dijkstra’s argument was not purely aesthetic. He contended that unrestricted goto made programs difficult to reason about, because the relationship between the static text of a program and its dynamic execution became hard to track. Verification, testing, and understanding all suffered.

The response was vigorous and not entirely unfriendly. Donald Knuth published a careful rebuttal in 1974 in Computing Surveys, arguing that goto had legitimate uses in specific performance-critical situations and that blanket prohibition was too strong a position. Knuth was right that there are edge cases. The debate was real.

But the broader point was correct, and most programming languages designed after the mid-1970s treated structured control flow as the default. The practice did not fully consolidate until the 1980s and into the 1990s, when languages like C, Pascal, and their successors had simply won the market. By the time a programmer in 1995 learned to code, goto was something you read about in history, not something you reached for.

Again: roughly twenty years from a clear theoretical result to widespread practice.

High-Level Languages: When Assembly Programmers Knew Better

John Backus and his team at IBM released Fortran in 1957. The pitch was straightforward: write mathematical formulas in something close to mathematical notation, and the compiler will generate assembly code. The efficiency argument was explicit in the design, because Backus knew that programmers would not adopt a high-level language that produced noticeably slower code.

The reception was skeptical. Assembly programmers of the era had hard-won expertise in the specific instruction sequences that made programs fast and small on constrained hardware. A compiler, they argued, could not possibly match human judgment about register allocation, instruction selection, and memory layout. The generated code would be bloated and slow. The compiler would miss the tricks that mattered.

They were partly right, initially. Early Fortran compilers did not always produce optimal code. But Backus’s team worked hard on the compiler, and the generated code turned out to be good enough for most purposes most of the time. More importantly, the productivity gain from not writing assembly was enormous. Programmers could express ideas directly rather than encoding them in the machine’s vocabulary.

The assembly programmer’s objection, that expert humans can outperform a compiler, was true for narrow cases and increasingly irrelevant at scale. By the 1960s, high-level languages were not controversial for most application work. The holdouts in systems programming took longer, but C in the 1970s eventually settled even that argument by providing a high-level language with enough low-level access to write operating systems.

From 1957 to consensus: about fifteen years, with pockets of resistance lasting much longer.

The Pattern and What It Suggests

Three innovations, three adoption lags of fifteen to thirty-five years. The pattern is consistent enough to be worth taking seriously as a predictive tool.

Wheeler’s survey also touches on formal verification, the practice of mathematically proving properties about programs. Robert Floyd’s 1967 work on assigning meaning to flowcharts and Tony Hoare’s 1969 axiomatic semantics paper laid the theoretical foundation for reasoning about program correctness. Leslie Lamport’s TLA+, a specification language for concurrent and distributed systems, has been used at Amazon Web Services to find real bugs in production distributed systems designs. Daniel Jackson’s Alloy provides a lightweight formal modeling tool accessible enough for ordinary engineers.

Formal verification has been “almost ready” for mainstream use for roughly fifty years. The tools are better now. The hardware is fast enough that model checking is practical on real problems. Companies like AWS, Microsoft, and Intel use formal methods on critical components. The knowledge exists. The tooling exists. The adoption has not happened at scale.

Rust’s borrow checker is interesting in this context precisely because it industrializes a subset of Hoare logic, the idea of pre- and post-conditions on operations, into a type system that enforces ownership and lifetime constraints at compile time. It is formal verification that programmers accept because it presents as a type checker rather than as a theorem prover. The adoption curve for Rust is steep but real, and what programmers are adopting, whether they frame it this way or not, is a form of static reasoning about program correctness that Floyd and Hoare described in the late 1960s.

TLA+ and Alloy may be in the position that generational GC was in 1984: technically sound, used by people who understand the problem, and waiting for a forcing function that makes mainstream programmers take the cost seriously.

The honest prediction from the historical pattern is not that these ideas will win in five years. It is that they are probably correct, and that the programmers who learn them now are in the position of the LISP programmers using garbage collection in 1965, aware of something real that the industry has not yet decided to care about. The lag is not a sign that an idea is wrong. It is often a sign that the idea is ahead of the infrastructure, tooling, or cultural moment needed to make it unavoidable.

Good ideas are patient. The question is whether the people carrying them are.

Was this interesting?