Reuse: Is the Dream Dead?
Reuse is software development’s unattainable panacea. The ability to compose systems from reusable elements has long been our achille’s heel. We want reuse so badly, yet our failures are spectacular. Almost all major technology trends of the past 20 years touts reuse as the saving grace. Vendors have sold billions of dollars in software through the broken promise of increased reusability.
What happened? Reuse was supposed to save software development. In the early 90’s, object-orientation promised to save us. It didn’t, so we moved on. In the late 90’s, component-based development promised to save us. It didn’t, and we moved on. Shortly after the turn of the millenium, SOA promised to save us. It didn’t, and we’re moving on. Why is reuse so damn hard?
The problem stems from the following rather simple statement, which is depicted in the diagram (click it to view in full size):
Maximizing reuse complicates use. (1)
In general, the more reusable we choose to make a software component, the more difficult that same software component is to use. In the extreme, an infinitely reusable component is infinitely difficult to use. Dealing with the tension between reuse and use is a complex issue, and often, we fail. Largely, the problem has to do with dependencies.
NOTE: I use the term component pretty loosely here. In general,
the software development community has done a poor job defining the
term “component”. Let’s just assume that when I say “component”, I’m
talking about a chunk of software. No more, no less.
The Reuse Disclaimer
I recognize that we’ve done a fair job in achieving reuse at certain levels, and we’re much farther along the reuse curve than we were 20 years ago. Today, we have a plethora of frameworks to choose from that aid development. Web frameworks, ORM frameworks, and security frameworks to name just a few. But most of these frameworks are horizontal, not vertical. They address problems related to infrastructure and plumbing code, not business problems. And I want to focus explicitly on vertical reuse, because that’s the unattainable panacea we’ve been struggling with for so long. That’s the broken promise. Why have we struggled to create reusable business components?
Granularity is the extent to which a system is broken down into parts. Coarse-grained components tend to be richer in behavior than fine-grained components. Because coarse-grained components do more, they tend to be bigger than fine-grained components. To maximize reuse, we try composing coarse-grained components from fine-grained components. Of course, this results in a lot of dependencies between components, making them more difficult to use. In general, we can say the following:
Coarse-grained components are easier to use, but fine-grained components are more reusable.
Time for an example. Let’s say we’re creating a component that processes health insurance claims. Let’s keep the business process relatively simple here to maintain our sanity. There are four steps in the process. First, the system is fed the claim information. Second, the system checks to make sure it’s not a duplicate submission. Third, the system reprices the claim based on HMO and PPO agreements. Fourth, the system remits payment. A coarse-grained component would perform all four of these steps.
In doing this, we’ve made it easy to use since we only need to invoke one operation to complete the whole process. But it’s also more difficult to reuse only a portion of this process, such as the remit payment code. The logical solution is to create four fine-grained components (one for each step in the process) and one coarse-grained component composed of the four others that pulls everything together. The fine-grained components make things more reusable, but are also more difficult to use since we have to do more to pull them all together to perform a unit of work.
Weight is the extent to which a component depends on it’s environment. A heavyweight component depends on it’s operating environment, while a lightweight component avoids these dependencies. When creating a component that runs in multiple environments, we’re forced to move the environment specific dependencies (ie. context dependencies) from code to configuration. This makes the component more reusable, but it’s also a bit more difficult to use since the component must be configured for each environment.
Designing and configuring a lightweight component is more difficult than simply dropping in a component programmed to operate in that environment. In general, we can say the following:
Lightweight components are more reusable, but heavyweight components are easier to use.
Let’s elaborate using the example above, where the solution was to create one coarse-grained component composed of four fine-grained components. If each of these components only needs to run within a single application in a single operating environment, we can encapsulate all of this environmental code into each component, making each heavyweight. But if we want to reuse these components across applications and operating environments, then we have to move this code outside of the component and ensure it can be configured for each environment in which we want it to operate.
Reuse or Use
The challenge we run into when attempting to create a highly reusable component is to manage the tension between reusability and useability. In our example above, breaking out the coarse-grained component into fine-grained components makes it more difficult to use each of the resulting fine-grained components. Likewise, creating a lightweight components makes using the component more difficult since the component must be configured each time the component is used.
Fine-grained components have more component dependencies and lightweight components have more context dependencies. Each makes a component more reusable, but also more difficult to use. The key is to strike a balance, and that is a topic for another day not too far away.
(1) This statement is a derivation of Clemens Szyperski’s statement in Component Software: Beyond Object-Oriented Programming - “Maximize reuse minimizes use.”