the simplicity of a limb
In their paper on Evolvability, Marc Kirschner and John Gerhart discuss the separation of concerns within our genetic code. They pay special attention to limbs:
The limb is a complex structure with precisely placed bone, cartilage, muscle, nerves, and vascular elements, and one might think it is difficult for such a structure to change in evolution.1
Every vertebrate uses the same genes to encode their limbs. This mechanism can describe wings, flippers, arms, and everything in between. But how?
Our genome can't contain separate descriptions of our bones, muscles, nerves, and so on. If it did, a mutation in one would need to mirrored in all the others. Productive evolution would be nearly impossible.
"The basic structure of a limb," it turns out, "depends on serial cartilaginous condensations." These condensations are both a scaffold and a signal; they ossify into bone, but also guide the development of the surrounding muscles, nerves, and blood vessels. Only one part of the limb is described in absolute terms; everything else is relative.
As a result, "[e]volutionary modification of vertebrate limb shape and size is reduced mostly to the mutational modification of the cartilaginous condensations. It need not be simultaneously accompanied by mutationally derived changes in the muscle, nerve, and vascular systems, which can accommodate to any of a wide range of limb sizes and shapes."
The limb is highly coupled, but still easy to change. This is because it has a locus, and that locus suffices. Our explanation can begin and end with the cartilaginous condensations.
This is a common strategy in software design. Consider the Thrift interface definition language (IDL), which is used to specify datatypes:
struct Classroom {
1: string building,
2: string room,
}
For each struct
, there is a corresponding binary format. The Thrift compiler can generate, in almost any language, a codec for that format. This generated code is long, repetitive, and usually ignored. The specification is a locus, and it suffices.
In the limb-metaphor, simplicity means opacity. The interface provides a language that can describe every problem, every solution. The underlying details are forever excluded from our explanation's suffix.
This view of simplicity, however, can sometimes lead us astray. Consider this named value:
const total_cost = ...
Like all names in software, this is an interface. We can reference total_cost
without knowing how it was computed. And when reading this expression, we can usually ignore the right-hand side. The name suffices.
But this interface is not opaque. Imagine if it were: the right-hand side of expression would be hidden away entirely. We would be unable to change the underlying computation; all we could do is change the name, and hope the compiler does the right thing.
No one wants this. Names and referents are, by design, part of the same structure. When changing one, we must consider if the other still fits. This is the shortcoming of most biological metaphors in software. Evolution is a blind watchmaker; we, on the other hand, can see.
And so, interfaces that conform to the limb-metaphor are most useful at the periphery of our software. They provide a terminus, a place where our explanation naturally ends. To understand the intermediate interfaces in our software, we need a different metaphor. Next week, we will explore the simplicity of a fractal.
This post is an excerpt from my (incomplete) book on software design. For more about the book, see the overview.