Entering the Zone of Pain
Editorial Note: I originally wrote this post for the NDepend blog. You can check out the original here, at their site. While you’re there, download NDepend and see if your code falls into the infamous Zone of Pain.
Years ago, when I first downloaded a trial of NDepend, I chuckled when I saw the “Abstractness vs. Instability” graph. The concept itself does not amuse, obviously. Rather, the labels for the corners of the graph provide the levity: “zone of uselessness” and “zone of pain.”
When you run NDepend analysis and reporting on your codebase, it generates this graph. You can then see whether or not each of your assemblies falls within one of these two dubious zones. No doubt people with NDepend experience can recall seeing a particularly hairy assembly depicted in the zone of pain and thinking, “I knew it!”
But whether you have experienced this or not, you should stop to consider what it means to enter the zone of pain. The term amuses, but it also informs. Yes, these assemblies will tend to annoy developers. But they also create expensive, risky churn inside of your applications and raise the cost of ownership of the codebase.
Because this presents a real problem, let’s take a look at what, exactly, lands you in the zone of pain and how to recover.
First, we’ll look at the easiest to reason about part of this equation. NDepend will tell you about the abstractness of your assembly.
Abstractness, in this instance, simply means the ratio of abstract (interface or declared with abstract keyword) to total types within the assembly. If your assembly contains many interfaces and abstract base classes, it will have a higher abstractness score. Likewise, if it contains none, the ratio will plummet to zero.
Lack of abstractness pushes you toward the zone of pain. Abstractions provide seams that allow for testability and for changing the code by adding members, rather than heavy modification. Without abstraction, modification becomes risky and difficult.
Second in our look at the zone of pain comes the concept of instability. In terms of code metrics, instability is a composite of two concepts: afferent coupling and efferent coupling. Before going further, let’s understand those two terms.
Both concepts of coupling relate to assemblies and involve the relationships between types in the assembly and outside of it. Efferent coupling tells you the degree to which types in an assembly use types outside of it. Afferent coupling tells you how many types outside of the assembly depend on types inside of it.
The ratio of efferent coupling to total coupling gives you the instability of the assembly. And low instability (or high stability) contributes to landing you in the zone of pain.
If this seems difficult to understand, let’s break it down a bit. Maximum stability occurs when an assembly couples entirely in afferent fashion. This makes intuitive sense, when you think about it. Imagine an assembly depended upon by every other assembly in your application, but dependent upon none. The weight of all of that inbound dependency would deter you heavily from changing it, creating a stable assembly. But that same, intense stability creates a situation where needing to change something can bring pain.
Pain: Concreteness and Stability
Notice that, with both stability and concreteness, I talked about them pushing you toward the zone of pain. I took care with phrasing because of the two dimensional nature of the graph. Getting stuck in the zone of pain requires both components in relatively high doses. But you could conceivably score high in one but very low in the other and still not find yourself there.
Still, I don’t advise seeing how close you can walk to the line. The graph, with its coloring, walks you toward a sustainable path of moderation, decorated with a green band. It suggests that you balance abstractness and stability by making highly abstract assemblies relatively stable and highly concrete assemblies relatively fluid.
Philosophically, this reinforces a ubiquitous theme of software architecture. Design decisions are almost always a question of tradeoffs. Different ways exist to land yourself in a world of modification pain with your assemblies, and not all are created equal.
Moving Away from Pain
Should you find yourself in the zone of pain, you have two basic courses of action. You can strive for more abstraction or for a narrower inbound dependency footprint. (I will indulge an aside here to say that you could, theoretically, ramp your outbound dependency footprint way up, but I submit that this constitutes gaming the spirit of the zone of pain graph.)
You will probably have an easier time striving for more abstraction. After all, you could make this happen trivially by simply adding tons of interfaces and abstract classes. But don’t game the graph. Instead, look for opportunities to introduce the aforementioned seams to your codebase, especially where inbound dependencies occur. Get yourself out of the zone of pain by forcing dependent assemblies to depend on abstractions instead of concretions.
The other main course of action involves reducing your inbound dependency footprint. If dozens of types throughout your codebase depend on dozens of types in this assembly, see if you can reduce the surface area of that relationship by concentrating the dependencies in fewer classes. Good candidates include classes with only a single method used here or there. Consolidate and make your life easier.
Take care that you don’t overdo it, however. If you go nuts with interfaces and assemblies, you run the risk of a confusing and over-engineered solution. And if you eliminate every inbound dependency, you will have a dead and useless assembly. In fact, the corner on the graph opposite the zone of pain has the name “zone of uselessness.” This, too, makes sense. A completely useless assembly is easy to change, since the consequences are nonexistent.
All Things in Moderation
This graph, as with any chart or metric, serves as a guide. You could certainly find codebases with assemblies that fall in the red and yet prove invaluable and well crafted. Similarly, you could take some abomination of an assembly and add do-nothing interfaces until the graph puts you in the green. But these activities miss the point.
Keep an eye on this graph with its zones as you work on your project. Over the course of time, you may see your assemblies start to drift into the orange and red sections. This occurrence should start a conversation about the maintainability of your codebase, and whether or not change will start to become painful.