One level of indirection at each layer
“Indirection” is pointing to something by name or reference, rather than pointing to the thing itself.
Indirection is everywhere in computing. Pointers are indirection for a location in memory. Dynamic dispatch is indirection for procedures. URLs are indirection for IP addresses. Indirection is so fundamental, there’s an aphorism called FTSE, “The Fundamental Theorem of Software Engineering”:
We can solve any problem by introducing an extra level of indirection.
So perhaps we can solve any problem with indirection, but it’s also possible to introduce indirection without solving any problem. From RFC 1925, The Twelve Networking Truths:
(6) It is easier to move a problem around (for example, by moving the problem to a different part of the overall architecture) than it is to solve it.
(6a) (corollary). It is always possible to add another level of indirection.
This is why FTSE is usually given a tongue-in-cheek postscript: “We can solve any problem by introducing an extra level of indirection… except for the problem of too many levels of indirection.”
So how many levels of indirection is too many levels of indirection? When are you just moving the problem around?
Reflecting on layered protocols, I think I have a hunch for a useful rule-of-thumb:
One level of indirection at each layer.
Why? Each layer solves a different problem. Introducing indirection between layers lets us slice the system into distinct areas of responsibility, and creates shearing points, where we can upgrade or replace a layer without breaking the system. Introducing indirection within layers is more often just more indirection, since the indirection is within the same scope of responsibility.
One level of indirection allows for loosely-coupled layers. A step change.
More than one level increases complexity for incremental value.
Infinite levels of indirection = halting problem. Turtles all the way down.
A concrete example: URLs name things, DNS locates them. Introducing an indirection between these responsibilities creates a useful property: