Kirk is a software developer who has filled most roles on the software developer team. He is the author of Java Design: Objects, UML, and Process (Addison-Wesley, 2002) and he contributed to No Fluff Just Stuff 2006 Anthology (Pragmatic Bookshelf, 2006). His most recent book, Java Application Architecture: Modularity Patterns with Examples Using OSGi was published in 2012. Kirk is a DZone Zone Leader and has posted 77 posts at DZone. You can read more from them at their website. View Full User Profile

Reuse: Is the Dream Dead?

07.09.2009
| 5098 views |
  • submit to reddit

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

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

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.”

From http://techdistrict.kirkk.com/

Published at DZone with permission of its author, Kirk Knoernschild.
Tags:

Comments

Andrew McVeigh replied on Thu, 2009/07/09 - 3:51am

this is a good article.  the core (technical) tensions that make reuse hard are, as you said granularity and the conflict between making something general (and complex) for reuse and making it simple and easy to understand.  as doug mcilroy said in the original '68 speech at the UN on components, if we haven't got components nailed within 10 years, maybe it's not a great idea and we should give up ;-)  here we are over 40 years later!

i've looked into these areas in some detail, as i was originally pondering at how to reuse components in my work on a large JEE system.  it led to my phd on components and extensibility.

anyway, here are a few answers:

1. the tension between fine-grained (more reusable) and coarse-grained (ease of use) components can be resolved using a hierarchical component system, where components are made up of other component instances.  you get both, and they are easy to manage. it changes the entire component story. see http://www.javalobby.org/articles/di-heritage/

2. as we build components out of other component instances, we unfortunately end up burying abstractions in the composition hierarchy.  one way out of this dilemma is to allow components to be modified in a way which keeps the changes separate from the original definition.  see http://www.eecs.ucf.edu/SAVCBS/2006/papers/McVeigh-Kramer-Magee.pdf -- essentially it's a form of inheritance between components.

(2) can also be used in part to fix up the dependency problem.   you can switch out component instances with dependencies that you don't like.

as a side point, interestingly i used the modification of an insurance system to demonstrate part of my research.  you also use a health insurance system as an example.

part of the problem stems simply from the fact that component is a very overloaded term. syperski tends to focus on components as being deployment artifacts primarily rather than design things. IMHO, his book results in more questions than answers.  it also ignored much of the academic research on components in architecture description languages that preceded the book by about 10 years.

 

Fred Gracely replied on Thu, 2009/07/09 - 8:00am

To my mind, there is also a misunderstanding about engineers at play in the re-use debacle. Most people assume that engineers love complicated technical challenges of any sort. The truth is that most engineers are creative people and find little joy in “wiring up” other people’s complicated stuff, especially when there is no guarantee the result will accomplish what is necessary. Faced with an opportunity to create and achieve the desired result vs. fiddling with somebody else’s overly complex abstractions, most engineers will choose the former. Re-use flies out the window. This is also why we have a proliferation of frameworks and components: they are lots of fun to create.  Of course, there is always the case of mandated re-use, such as within an organization to ensure common approach, technology, etc. Even there, however, creative nature will find a way to subvert if the proper pressure is not applied.

 

Great article! Thanks!

Rogerio Liesenfeld replied on Thu, 2009/07/09 - 9:11am

I think the real problem is where to focus development/design efforts.

Instead of focusing on reuse, focus on avoiding duplication. Don't ask "how can I make this piece of code or functionality reusable in a different context or application", but "how can I avoid solving the same problem multiple times in the same context/application". And the great thing is that code duplication can be detected by tools, and therefore automated in a daily/continuous build (using the CPD tool, Team City, etc.).

Focusing on reuse often leads to over-engineering and pointless abstractions and code. Focusing on avoiding duplication leads to smaller and more maintainable code bases, and on truly useful abstractions.

Oliver Plohmann replied on Thu, 2009/07/09 - 9:48am

IMHO, There is some degree of reuse possible in the realm of "technical objects" (collection clases other kind of base classes) or frameworks. This is because the problem domain is the same whether for example addressed with the use of hibernate or with iBatis.

But one of the initial ideas that you can reuse business objects have meanwhile shown to be wrong. The idea of business objects was that you can buy some credit object from some software supplier and then create your credits system by connecting those business objects. One reason this did not work is that the problem domain is not the same for two different banks, for example. Two different banks look at the credit business in a different way and therefore want their credit systems to work according to their needs. Now comes the difficulty how to make a credit business objects that fulfills all needs, especially when you don't k now all the requirements in advance (typically the customer figures out the requirements while the project is under way ...). If you know a way how to do this, I'm sure SAP will pay big bucks for this. But when you develop hibernate or iBatis this issue does not exist: a relation database is the same for both of them.

I remember that many big software suppliers annouced to offer packages with business objects. As this turned not to be feasible they called their database interface framework a business object and everything was fine. This taught me a lesson not to tell the customer that he is dreaming, but to deliver something in a nice box that says "business object" on it. A customer like a bank that often pursues politics like "everything is doing fine and we are all doing well" will not complain (most likely not understand). At the age of 40 you made the money to retire and spend a happy life working on your own open source project ... A little bit of sarcasm helps to show the truth ;-).

I remember what a former colleague of mine said: modularity and reuse are the same thing. I think he was right there.

 Cheers, Oliver

Howard Lewis Ship replied on Thu, 2009/07/09 - 11:52am

In Tapestry, we have quite a bit of true component reuse ... but that's because the environment and lifecycle for components is so highly constrained.  That's a good thing. Still, the built-in components can only stretch so far to application-specific needs, at which point you need to build custom (or at least subclass).

 I think anyone using an IoC container (Spring, etc.) is seeing a lot of reuse.

 Elsewhere, what I'm seeing is reuse is much harder without a lot of infrastructure around the edges.

Part of the promise of functional programming is enhanced levels of reuse; in Clojure all data is basically untyped maps and lists, and by using and reusing built-in functions to slice-n-dice such data, you get some great amounts of reuse.

Somehow the idea of reusability got attached to object-oriented programming in the 1980s, and no amount of evidence to the contrary seems to be able to shake it free.
-- Paul Graham
 

Developer Dude replied on Thu, 2009/07/09 - 11:58am

I agree with Olli.

It is one thing to come up with reusable ORMs, collections, I/O libs, servlet containers, etc., (and even there - how many different servlet/application containers do we have?) - it is another thing altogether to come up with reusable business domain objects. We can't even agree on what an address or a telephone number is (how many devs have written their own variation of these, usually a new one for every new project, or at least at every new job? Probably every one of us), much less what a billing or ledger entry is, not to mention business domain concepts that are even more specialized (what is an office visit for a dentist, v. one for an MD, or even a lawyer?).

I think we can come up with some agreements on such things as addresses, telephone numbers - after some research, review and iterations, but vertical business domain objects? Not in the near future IMO (I don't know enough about DSLs to say if they are a possible solution - but the idea of domain specific definitions has been tried before with XML, and I don't know very many projects that have used those schemas).

I have seen very little horizontal reuse in most of the projects I have worked on (except for rusing things like languages and third party libs), even when it was fairly simple, relatively pain free, and really obvious how to implement the reuse. Everybody has their head down in the code, writing the same thing over and over again to bother with reuse. The bigger picture of reuse in the vertical axis is totally ignored. Good luck with that.

Johannes Brodwall replied on Mon, 2009/07/13 - 7:19am

I've seen the same problems with horizontal reuse as Developer Dude mentions. I think it's caused by the problem that you can basically not achieve the following at the same time:

  • Reuse
  • Quality: The code is fit for purpose, and as Kirk mentions: usable. In order to be good, code has to evolve.
  • Stability: There's not a lot of disturbing changes coming from others. Ultimately, this will lead to branching and possibly, completely forking of the code.

In order to have good code, it has to evolve, but changes will cause problems for other users of the code and other people's changes will cause problems for you. Ultimately, we may decide to split the code again and stop reusing.

I wrote a bit more about this on my blog a few months back.

As a counter point to the "doom and gloom" about reuse, I'd like to point out that the standard Java library that we reuse every day is much, much bigger than what we'd expect from a language ten years ago. So we are reusing much more. This also means that the "marked" for internal reusable code is smaller. The easy cases have been covered already.

Developer Dude replied on Fri, 2009/08/07 - 7:12pm

Regarding changes to reused code, churn, impact, etc.: I think it is important to write to interfaces where possible and to change your interfaces minimally. Good API design also means being consistent about method names, argument types and patterns. For example, most of my DAOs look the same: they consist of a number of CRUD methods literally named Create, Retrieve, Update and Delete and I try to have the variations of these make sense for those methods that return one item v. a list. We have all seen APIs (including the JDK) where there are methods that start with 'find', others that do the same thing that are 'get', and yet others that start with 'search'.

I am getting to the point where I won't do 'isSomething' anymore because it is not as discoverable as 'getSomeBoolean'. Sure the former may be more semantically correct and human readable, but anybody who has ever written code to interface with a bean/POJO with getters/setters (which is almost anybody who has written Java code) has quickly come to understand the 'get'/'set' convention.

So some simple to implement/comprehend practices for writing code can minimize the unnecessary churn/impact of changes.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.