The Navigable City and Knowable Neighborhood of Good Software

I’ve been thinking about this paradigm of navigability and knowability in software for a couple weeks, and thought it might be interesting or helpful to others, so here goes.

Consider 2 towns. The first, Tinyton, is a town of 150 residents, with 5 windy streets that cross back and forth over each other. It has a general store and a diner. The second, Bigville is a town of 15,000. It has a design pattern of gridded streets, with a convention of numbering them, and a few dozen businesses scattered around the town.

Tinyton is knowable. It’s small enough that it is reasonably possible to know every place in the town, and how to get there from anywhere else, at least with a few well known landmarks. However, it is not very navigable. If you’re not already familiar with it, you will get lost a lot.

Bigville on the other hand is navigable, though less knowable. It is unlikely that anyone can learn every place of note in the town, but because the streets are predictable and meaningfully labeled, as long as you know where you are and where you mean to go, you can find your way pretty easily. Within Bigville, neighborhoods will be knowable, but not likely the whole town.

As with urban planning, design patterns and conventions in software should also serve navigability. They should make it easy to find your way around a codebase, and make sense of what your looking at in any particular piece of code you maybe looking at. Architecture should explain why things are where they within a system, but not really what they are or what they do.

Going back to the town analogy, let’s drill down a little farther here and imagine within the Bigville there’s a store called Mike’s Deli. Mike was a plumber, so his deli sells plumbing supplies and work clothes, then has a small deli counter in the back that makes sandwiches. Within the context of his experience, this abstraction provides a useful and meaningful encapsulation, but to others, its highly specialized and poorly scoped, and the name is an obfuscation.

Mike also runs his store out of his garage, so it doesn’t conform to the patterns and conventions of the town. This makes it harder to use as well as being hard to understand, as it requires special knowledge and handling compared to everything else in the town.

Again translating to software, it’s important to remember that while undeniably valuable, encapsulation and abstraction are inherently forms of obfuscation that increase the cognitive load of working with a system. Additionally, they introduce opportunity to violate architecture. Put another way, when things get DRY, they get lighter, but also often harder and more brittle. Try to make sure your encapsulations minimize cost to benefit given this.

Also, when considering abstractions and encapsulations, try to generalize them as much as is reasonable. The less local context a visitor to your neighborhood of the codebase needs to make sense of things, the better.

I hope the navigability/knowability paradigm is a useful framing for others when thinking about software they’re building, and that I’ve done something to help illuminate the cost calculus of encapsulation. In the least, they seem useful for framing code review conversations.


5 responses to “The Navigable City and Knowable Neighborhood of Good Software”

  1. I appreciate the article.

    I disagree with the following statement:
    > while undeniably valuable, encapsulation
    > and abstraction are inherently forms of
    > obfuscation that increase the cognitive
    > load of working with a system.

    They are valuable and they are forms of obfuscation.

    When employed proper, however, they do not increase the cognitive load of working with a system. Indeed, their value lies precisely in the notion that they decrease the cognitive load.

    Like

    • I suppose I should have been more explicit. I agree that *when implemented properly* abstraction has a net negative impact on cognitive load, at least within a local context (aka within its neighborhood). The point I was making is that there’s a cost inherent in introducing an abstraction in a system, and that that cost must be overcome in the benefit gained from the abstraction. Too often, I’ve seen poorly scoped code wrapped in a function to enable reuse or some form of logical separation, and I was more going after those poorly thought through or marginal uses of encapsulation.

      Liked by 1 person

  2. I appreciate the blog post.

    I disagree with your assertion that “encapsulation and abstraction … increase the cognitive load of working with a system.” While it is true that they can increase the cognitive load, if they do so, it is a clear indication they are not being done correctly.

    The value encapsulation and abstraction offer is that, when implemented properly, they actually _decrease_ the cognitive load of working with a system.

    Few systems are simplistic enough that they can be fully understood without abstraction and encapsulation. If that were not true, we’d still program in assembly language and forgo higher-level languages altogether.

    For example, imagine trying to explain a Gnu/Linux system (including the hardware) without the benefit of abstraction and encapsulation. Few would listen long enough to detect when you got confused and lost because the information overload would drive them away long before that.

    Like

    • The *when implemented properly* bit is key here. Too often, I’ve seen code just wrapped in a function with little thought. This is spaghettification through encapsulation. I perhaps should have been more explicit, but my point was more about how to think about encapsulation to try to ensure you do implement it properly, more than saying it’s bad on its face.

      Like

Leave a comment