Abstraction and Implementation

Paul Dourish, in his book Where the Action Is:

Software systems are built from abstractions. The extent to which abstraction is fundamental to software systems can be hard to explain to someone who hasn’t built them; while to someone who has, it can be so ubiquitous as to have become invisible. But the very essence of software system design is the manipulation, combination, and creation of abstractions.

Essentially, computers manipulate information in terms of the presence or absence of energy, which we render as ones and zeros. There’s a wide gap between this binary way of representing reality and how human beings experience reality. We bridge the gap using abstraction.

The essence of abstraction in software is that it hides implementation. The implementation is in some ways the opposite of the abstraction; where the abstraction is the gloss that describes how something can be used and what it will do, the implementation is the part under the covers that describes how it will work.

Ones and zeros are implementation; icons, widgets, labels, etc. are abstractions. It’s easier for us to relate to and manipulate icons and widgets than sequences of ones and zeros. Dourish offers a more relatable “real world” illustration of the distinction:

If the gas pedal and the steering wheel are the abstraction, then the engine, power train, and steering assembly are the implementation.

We can represent such a system digitally as an assemblage of conceptual objects: “entities whose essence lies in the range of manipulations they allow, and whose inner workings can be ignored as long as the external constraints are maintained.”

Abstractions allow us to interact with complex systems without needing to understand their “inner workings.” By hiding the messy details from users (and from ourselves), abstractions enable us to design and build complex systems.

Hiding the implementation and dealing with something in terms of its abstraction allows us to isolate one piece of a system from all the rest, and so to adopt a modular approach to design that sees the system as an assembly of interoperating components…

The distinction between abstraction and implementation is easy to see when presented in stark terms, such as the difference between ones/zeros and icons/widgets. But complex systems often have several layers of abstraction:

  1. machine code abstracts ones and zeros,
  2. low-level programming languages abstract machine code,
  3. high-level programming languages abstract low-level programming languages,
  4. etc.

For Dourish, this software stack is “a tower of mutually constituted abstractions, right down to the abstractions of binary logic that we use to isolate ourselves from the messy world of continuous voltages and variable current.”

In practice, the difference between the tower’s layers can be unclear. It’s easy to confuse a system’s conceptual model with its implementation model. It’s also possible to take abstraction too far.

So, it’s important that designers understand the difference between abstraction layers. We must learn to focus on the level most conducive to effective human interactions — while acknowledging the constraints and opportunities afforded by our particular “tower of mutually constituted abstractions.”