Back in April, there was an interesting article quoting Ron Jeffries et al on InfoQ looking at code reuse from an agile perspective. The conversation steered toward explaining reuse as a concern that is very expensive, looking at it from a top-down, "enterprise"-wide lens.
But is code reuse a black and white issue? My contention is there are varying degrees of reuse that are often neglected on a microscale within an even moderately-sized project. If the code you produce isn't going to be reused, is it even going to be looked at? These are similar problems with similar solutions.
As far as producing reusable code, I believe there is a "happy medium" between full-fledged, framework or API reuse and also a "just enough" reuse. The article touched on a similar concept:
Adam Sroka called this ‘emergent reuse' and noted that this seemed more efficient than the ‘design for reuse' approach. A poster named Tim explains it to his business people this way: "The first time you have to pay for it to be coded, the second time you have to pay for it to be reusable, the third time it's free."
A higher degree of reuse requires more of an investment in these principles and practices. What's more, I think the article fails to draw a distinction between the roles (or competing concerns) involved in reuse: not only should we be worried about reusing code we produce, but often we are presented with a very real problem that wasn't talked about in the article which is how we consume code that we choose to reuse.
A project is itself a consumer of its own code, and many times you'll find a codebase is not even internally consistent in how it reuses code within itself. Quite often consumer-reuse happens in the form of depending on third party libraries, open source frameworks or APIs. In this sense, reuse becomes an issue of code quality, or more aptly, code cleanliness. It's hard to come up with solutions to big problems by just thinking about an ideal solution, and even if you are able to do it, odds are your solution will probably be rather academic. It's easier to make progress on large-scale problems by breaking them down, and making a habit of looking for patterns and ways to combine, compose and chain smaller solutions. Like we do with Agile problem solving approaches. Can't we do that for the hairy reuse problem?
Decomposing the Problem
Code reuse is a tough problem to nail down, because it's a high-level view composed of a whole host of smaller concerns. With a nod to separation of concerns, I've tried to decompose these competing factors into their own categories and make some suggestions of things to look at if you'd like to have a higher degree of reuse within your team or code base.
Last night I listened to an Agile Richmond talk by Jeff Sutherland espousing the ideal of communication saturation. Jeff was talking about Scrum, and wanted to draw a strong correlation between this saturation and "hyper-productive" teams. The key, Jeff said, was the ability of the group to recognize what is the next most important thing to work on and come to a collective decision about it. This resonated with my experience with agile. Our group was able to defer stories where we saw reuse was going to be apparent in the future, and this not only did this save us time in the long run, but it made the problem of "discovery" and consumption a no brainer. Everyone on the team knew a component we delivered one release was to be reused in future releases, even if we weren't involved in developing it. This, coulped with the idea of collective code ownership, opens all the right communication channels for a software development project in my opinion.
Value cross training
Don't be afraid to ask for help or give it if you can provide help. Educate yourself and teach others. You'll find that successful teams do this naturally through many different ways. If it seems tough to approach individuals, a better course of action may be to schedule a weekly development meeting to discuss hard problems and solutions to those problems. Record topics that come up regularly. An informal retrospective after a release or milestone can go a long way to correcting the course of the ship.
Cross cutting concerns
A whole class of reuse problems can be boiled down to reinventing the wheel instead of solving core business problems. If you're solving a technical problem, is it a cross-cutting concern? If you can identify a problem that has been solved in more than one way, it's a good idea to pull the team together and talk about a good way to solve it moving forward.
Refactor mercilessly to remove duplication
Refactoring to remove duplication is a skill that takes practice and diligence to do well. But it's often a quick win to spot and remove duplication; Don't Repeat Yourself will go a long way to improving the quality of the code your team produces by coercing the design of the code into something more fundamentally sound and amenable to larger scale refactoring later.
Reuse problems are often a manifestation of not having "everything in its right place." If you always know where to go to get a hammer, then the problem is reduced to that hammer being there when you go to get it. The bigger problem is (rightfully so, now) knowing when you need that a hammer. This is not necessarily going to solve the problem of discoverability or consumption for you (communication is needed for that), but it will help with organization tremendously. Whether or not you achieve an enterprise degree of reuse may be a horse of a different color and have a lot to do with communication, but in the microscale refactoring is the weapon of choice.
If you are able to refactor using composed methods, naming becomes a lot easier. Each method will aim to solve one single "atomic" concern, and larger methods will become more readable and logical as they get shorter and more maintainable.
Know the language and learn standard APIs
Unit testing can help you learn a new API fairly quickly and document some hard-earned pieces of knowledge going forward. A handy construct that is available in many languages is a shell interpreter like IRB or BeanShell, which can be invaluable in helping to pick up the syntax and semantics of a language. Wikipedia has a list of interactive programming language shells.
Modularize your code
Modularization is important. Few languages support full modularization OOTB. At the base level, you need to be able to encapsulate your code and also package or namespace it. This is all you really get OOTB in most languages. Ideally, full modularization would provide you with the ability to bundle together code and provide a mechanism to have the code installed or uninstalled dynamically at runtime – in order to achieve this you often have to specify dependencies between modules and declare a version for your bundle. (OSGi aims to do this in Java, and is definitely worth a look).
Modularization is important to code reuse because it presents a barrier to someone else using your code. If I want to use a particular component or method that exists in your library, that means I am fundamentally absorbing the lowest-level dependencies that you have. If your project is not properly modularized, I may have to eat all of your dependencies. The larger your component is, the more complexity any consumer must absorb to reuse your code. Modularization should help with this problem.
SOA was supposed to have solved this problem, right? At least a subset of reuse problems – iteroperability. We've found it's a lot harder in practice than it is in theory. You have to version services, and use some registry to look up and discover a version. Not to mention the governance issues involved. This is why I'm so excited about OSGi. This might be the way to take your microscale reuse to the enterprise, and the model (in practice, and arguably theory) is a good deal simpler than SOA, too.
If you are writing an API or framework, you can't argue that documentation will be incredibly important to the success of your project. Note, I'm not talking necessarily here about Java-doc style-documentation; rather, I'm talking about providing a would be reuser of your code with enough support to make them successful. Sometimes that means concise, well written and up-to-date API documentation. Quite often this will come in the form of a user or developer's guide with a more narrative style or structure. FAQs or troubleshooting guides are often very important tools.
Reuse is something that is far easier to say than to do. Doing it requires both good design and very good documentation. Even when we see good design, which is still infrequently, we don't see the components reused without good documentation.
-D.L. Parnas, Software Aging. Proceedings of the 16th International Conference on Software Engineering, 1994
We all know waste is a thief. Rework is wasteful. Solving the problem of reuse on a grand scale may seem intractible, but I think it's just another problem, and arguably not even the hardest one we have to contend with in software development. If we can break the reuse problem down from its mighty enterprise-wide incarnation into smaller, more digestable reuse problems, it becomes a lot easier to make progress. And maybe one day we'll find a way to get to that enterprise-wide reuse that may seem too good to be true at the moment.