data Blog = Blog { me :: Programmer, posts :: [Opinion] }

Abstraction is Okay, Magic is Not

You encounter a crusty old-timer who tells you how great programming used to be: the computer just did what you told it. Languages were simple and easy to understand. In awe, you ask: “Did you write FORTRAN? or assembly?” The old-timer laughs: he’s not that old. He started with C!

It’s common to think of C and other “old” languages as being “close to the machine,” but that’s because the Unix abstractions have become so ubiquitous that we no longer think of them as abstractions. They feel natural, and they don’t leak until you need to do a lot of math or figure out why your program is so slow.

Magic is different. It feels different. When you encounter something magical, it may work, but you aren’t sure why. Worse, you can’t trust it. A hallmark of magical frameworks is being slightly “wrong:” you need to specify that you are expecting a Token prefix in the Authorization header, but the @Authorized annotation only lets you check for a Bearer token and where are those classes, anyway?

A successful abstraction is presented as a set of tools. There may need to be escape hatches, and a good abstraction includes semantics around them that, if not elegant, are at least usable. The places where “leaks” occur are signposted.

Magic, on the other hand, is presented as certainty. It isn’t a set of tools, but a holistic solution. Any escape is going to involve dark arts.

Abstractions don’t spring from nothing. They require a well-understood domain, and successful ones are often narrow because of that. The more detail and edge cases that must be dealt with, the more complicated an abstraction must be to deal with them.

When I’m writing code, I try to consider whether I’m solving the problem at hand or trying to solve the problem of solving the problem. I think the latter is a hallmark of magical approaches: “I have a perfect understanding of what’s necessary to solve this class of problem. I’ll make it Really Easy and nobody will have to think about it again.”

That’s not what happens, though, is it? If you give in to the temptation, you’ll write something that obscures rather than revealing intent. Your successors (including your future self) will curse you. If I find myself slipping into that mentality, I back up and think:

  1. What are the “nouns” of this problem?
  2. What are the “verbs?”
  3. How can I present a “sentence” that makes my intent clear?

Next month, there may be a noun I overlooked that needs to be handled. There may be a new operation that needs to be implemented. It’s very unlikely that the thousand lines I wrote to “solve the problem in five lines of code” will bend to fit as easily as a module, a couple of types, and a half-dozen functions will.