Writing software is hard, particularly when the schedules keep programmers “nose to the grindstone”; every so often, it's important to take a breather and look around the world and discover what we can find-ironically, what we find can often help us write software better.

Programmers, software designers, and architects constantly talk about the need for “good abstractions” and “well-defined models” of the software being built, but recent readings have led me to wonder whether we're using the idea correctly.

It's not uncommon for software guys to argue-for days at a time, in some cases-over the “abstractions” being written. Everywhere you look in software, we find abstractions. Everything in software seems to be an abstraction over something else: objects are abstractions over databases, databases are abstractions over one or more files, files are abstractions over file streams, which in turn are abstractions of the data moving across the peripheral bus (itself an abstraction) that communicates with the hard drive, storing bits of data in (you guessed it) abstractions of sectors, tracks, blocks, and who knows what else. Drill down far enough, and sure enough, it all turns into 1's and 0's. But even those 1's and 0's are abstractions for an arbitrary decision made somewhere around the “big bang” of the computing universe, to consider a certain amount of positive current to be a “1” and a certain amount of a negative current to be a “0”. Maybe. I'm not sure-I'm a software guy, I deal in abstractions, not a hardware guy who ends up watching those voltage fluctuations in oscilloscopes which dance around in patterns that, while pretty, mean absolutely nothing to me.

I'm a software guy. We use abstractions. Keep your bits and bytes away from me, man-I deal in managed code. You know, that code that runs on a virtual machine, which is sometimes executing inside a virtual machine, which in turn is running on top of an operating system (itself an abstraction over the machine), which is often running in some kind of virtual machine environment on the chip.

With all these abstractions in place, it's a wonder that anybody in the software world realizes that it's all just 1's and 0's in the end. Or that anybody can understand what we do.

The Grid

Actually, it's pretty obvious that they don't. I could cite examples of conversations with everyday people (I refuse to call them “users” for purposes of this article), but instead I'll point to an even more accurate source of popular perceptions.

Anybody here remember the opening scenes of the recent TRON movie sequel? You know, the one where they took Jeff Bridges (my twin from a distant universe) and reverse-aged him to look like he did in the first one and had him play both himself (the good guy) and the program-copy of him from twenty years ago (the bad guy)? If you don't, no worries: go grab the TRON:Legacy soundtrack from your favorite music store. It's good coding music.

Cue up “The Grid” and listen to the little backstory speech he gives.

The Grid. A digital frontier. I tried to picture clusters of information as they moved through the computer. What did they look like? Ships, motorcyles? Were the circuits like freeways?

Waaaait a minute. I realize this is a movie, fictionalized to Hollywood's weird, distorted tastes, but this is a little over the top. When's the last time you kicked off a build and saw a little motorcycle light-cycle take off across your screen, complete with cool energy trail to kill all those other motorcycle light-cycles trying to use the CPU? Or clicked “Save” in Word and saw a big ol' supertanker full of article data shove off and sail across the bus to rendezvous with the stream on the other end of a buffer?

Yes, I'm mixing metaphors here. Or, if you prefer, all of our abstractions. If you're having a hard time keeping them all straight, then imagine how the people who don't live with this stuff all the time must feel.

If you want to experience this feeling firsthand, go find a college quarterback and ask him to walk you through a play of his on video, complete with the jargon. Heck, high school, even. Anybody who suggests that football quarterbacks aren't intelligent has never played the game. (Whether they can pass an English exam or an accounting class is another question entirely.)

Flickering Shadows

In many ways, we imagine the creations of these abstractions to be a kind of noble art, something that we do to the greater good of humanity. I've seen (in fact, I've made) the comparison of the software abstractions we build to the “flickering shadows” that Plato used to describe the concept of “forms,” the abstract essence, if you will, of the material world around us. The analogy he used is that we are men in a cave, sitting with our backs to a fire, only able to view objects passing in front of the fire (but behind us) as shadows flickering against the wall. We can glean the basic shapes, but without any degree of accuracy, and none of the nuance.

The rationale, then, is that the closer our models can get to the “real world” the better. The deeper the level of nuance, the more accurately the model reflects the business world, the more successful the software will be.

Mathematics

The Greeks had a different notion of abstraction. They were really the first civilization to notice that whether you had three rocks, three pigs, or three acres, an abstract idea of “three-ness” was present, and it didn't really matter whether you were adding together rocks, pigs, or acres (or even some combination of all of them): the concept of combining “three” and “three” yielded “six” regardless of what kinds of things we were combining.

That led to all kinds of interesting realizations. It meant that we didn't have to think about the concrete aspects of things, which led to the development of geometry, for one. I know, I kinda hated geometry in school, too, but when you think about it, somehow these old guys in togas figured out that if you have a right triangle, the squares of the right-angled sides will always equal the square of the opposite side. Every time. Without fail.

And they did this not by simply writing test after test after test, and if a certain number of these empirical tests pass, declaring that it must be so for all numbers a, b and c. The Greeks did this by rigorous application of logic, relying on basic axioms (fundamental truths of mathematics) and derivations and applications based on those truths. What we take as a fundamental truth-the Pythagorean Theorem-is itself based on rigor and logic and proof. Lots of geometric “foundations” are in fact derivations.

Interpretations

The Greeks were famous for their schools and centers of learning, most of which got absorbed into other cultures when the Greek Golden Age came to a crashing halt after some 50 short years in the historical spotlight. Lessons abound in this Grecian view of the world that we can utilize for software.

For starters, notice that the power of math came when rather than adding to the mental model, the Greeks stripped it down. They reduced it to its simplest essence: just the number, with no units or attachments. That suggests that maybe, just maybe, sometimes the right thing to do in a model is strip it down, not bury it underneath more levels of abstraction.

This is part of the problem I have with object/relational mapping tools: by the time we get through all the code to put the string into the table, it's been through dozens of classes and numerous transformations. If the data wants to be stored relationally, why not present it that way? And if it doesn't want to be presented that way, then why store it that way?

The other lesson to learn is that simple empirical evidentiary “proofs” are nothing but: no matter how many tests we write, we haven't “proven” the software safe unless we can somehow, based on logic and some core axioms, reason out the results apart from the actual units. In other words, it is possible to have software failures despite unit tests that cover 100% of your codebase. Yes, unit tests will help give us evidence that the cases we anticipated are covered. And that's a good feeling, no doubt about it. But that's not “proven” correct.

Type systems, as it turns out, are the programming language's attempt to provide that mathematical basis for determining a program's correctness, and even there we run into some distinct cliff edges. It has become fashionable in the past decade to discard type systems and rigorous type checking by a software system designed for such checking (e.g., a compiler) in favor of languages that “get out of the way” and a scaffolding mechanism to run a piece of the system. Instead, we look at the results (e.g., a unit test).

The ancient Babylonians used the value “3” to describe the magic number used when calculating the area of a circle, because it was the closest approximation they could make to the value pi. It was good enough for their use, but that didn't make it right. And without that, they were never going to be able to progress. They chose to remain mired in the concrete, and not bother with the abstraction, because to them, numbers were about counting sheep and rocks and land.

The Greeks took an abstraction, simplified the system, and a whole new way of thinking opened up.

And that, my friends, is what an abstraction should do.

(For those interested in a light refresher on math combined with the place in history and historical context surrounding these discoveries, I highly recommend the book, Mathematics for the Nonmathematician, by Morris Kline.)