A big step in solving the software crisis is to accept that programming is not about computers. Yes, it used to be, and still is for brave souls working on operating systems or compilers. But in the long list of the world's software struggles, compilers rank pretty low.
It's easy to think programming is about computers because that's where we see problems. Of course, these are only symptoms of human error. Developers are constantly stretching to express behavior in terms friendly to a computer, making life harder so we can be easier on our machines. Such problems have been pointed out decades ago by the likes of Dijkstra, Backus [pdf], and Abelson and Sussman. Surely there is a way to describe our systems better than the terms of the von Neumann architecture.
For years we were limited by the capabilities of the machines, but this limitation has vanished for most of us. We've entered a new era, where improving the way we build software is as much a social problem as a technical one. Fortunately, there are signs of a growing social bedrock that could finally help us.
The Thirst for Abstraction
For a long time I didn't understand Guy Steele's comments on how Java can pull C++ developers about halfway to Lisp. Surely that fraction must be off; writing code in Java certainly feels a lot closer to C++ than Lisp. But independent of the Java language, the platform did a major service for the industry: it proved to the masses that we no longer need to express our designs directly in terms of the von Neumann architecture.
The abstraction offered by Java-like languages is small but significant. The conventional wisdom in software has long held that code must be expressed in terms of the machine to achieve passable performance. This myth is gone. Nearly every major software company is adopting some form of garbage collected, virtual machine-based language. We rely on the platform to map our higher-level expressions to efficient machine code.
This is a big step because accepting this level of abstraction opens the door to others. Why should we explicitly state data type information, when it can be inferred at compile or run time? Why not abandon primitives and arrays for objects and lists, since our platforms can now optimize away unneeded allocation? Why struggle with keeping track of many small state changes when major operations could change state atomically? Such abstractions make life easier for us, and previous successes suggest it will work. The result is a self-perpetuating thirst for abstraction. Expressing designs so humans can easily reason about and manipulate them is addicting.
von Neumann's Long Farewell
For many of us the shift away from the von Neumann architecture is painfully slow. Unfortunately the best ideas are often slow to be adopted. People are used to the way they've worked before, or averse to risk. So our transition toward better software is incremental -- but it is happening. Mainstream languages are offering higher-level concepts in their latest revisions. Adding concepts like closures, type inference, and applicative libraries does the software industry a service. It moves us toward a better way to write code. Hopefully some day we'll realize the low-level complexity we've dealt with isn't really necessary and abandon it altogether.