In software engineering, abstraction proves a difficult concept to grasp. It’s often considered to be synonymous with vagueness.
How can the concept of abstraction in software engineering be so difficult to understand and apply? I’m currently supporting a software development team working hard at bringing their engineering practices into the 21st century. Their challenge is to develop next-generation, highly complex software. To this end, I’m introducing them to some fairly standard software engineering concepts, including abstraction, decomposition, compositionality, components and interfaces. Some members of the team are taking on these concepts like ducks to water. But for many, the penny hasn’t yet dropped.
As a simple introduction to the basic ideas, I used the analogy of a guitar foot pedal – of which I happen to have a couple sitting next to my desk. A guitar foot pedal has configuration parameters (the knobs on the pedal), state behavior (on/off function), input data (signal from the previous stage), output data (processed signal) and error reporting (fault LED). The nice thing about the guitar pedal analogy is that, in principle at least, pedals have a common interface and that they’re usually composed together in series, the output of one pedal being plugged into the input of the next. Thus, we have a simple example of a compositional system. Further, with a little abstraction added, the pedals can be considered to have a common interface (input, output, configuration, on/off, error) and one can explain that the interface to a component is decoupled from its function – two pedals with the same interface can perform different signal transformations, eg reverb, delay or fuzz.
When I presented this analogy to the team, it led to a very useful discussion about testing. The test and integration guys came to realize that they could test each guitar pedal on its own, based on the specification of its interface and a definition of its function. Further, they could test the composition of multiple pedals in a similar way. The architecture guys came to realize that their job was to specify the interface to and define the function of each pedal, based on requirements and other criteria.
But when I added a little more abstraction and came up with a software model describing a pedal interface, I began to lose people. One of the test team put his hand up and asked, “But where’s the footswitch in your model? Where are the knobs?” When I began to probe the team’s understanding of the concepts I’d presented, I found that many of them understood the guitar pedal analogy but couldn’t fully make the link with software architecture and design.
For these guys, “abstraction” is another word for vagueness and decomposition is something that happens on an episode of CSI. For some, vagueness is a necessary comfort blanket that allows them to postpone thinking about tricky problems until they’re someone else’s problem. For others, vagueness is an anathema, and they want to see immediately how, for instance, the foot pedal switch connects to the software on/off function. The consequence is confusion and misunderstanding about precisely what amount of detail is appropriate at each level of decomposition of a software system. Establishing clarity on the subject is necessary if the team is going to successfully engineer complex software.
In my view, abstraction is about defining an appropriate level of detail at every level of decomposition by using encapsulation and fully specified interfaces. Or, at every level of decomposition, everything should be made as simple as possible, but not simpler. This isn’t that difficult to understand, is it?