James Michael Hare

...hare-brained ideas from the realm of software development...
posts - 166 , comments - 1494 , trackbacks - 0

My Links

News

Welcome to my blog! I'm a Sr. Software Development Engineer in the Seattle area, who has been performing C++/C#/Java development for over 20 years, but have definitely learned that there is always more to learn!

All thoughts and opinions expressed in my blog and my comments are my own and do not represent the thoughts of my employer.

Blogs I Read

Follow BlkRabbitCoder on Twitter

Tag Cloud

Archives

Post Categories

.NET

CSharp

Little Wonders

Little Wonders

vNext

Code Reuse is (Damn) Hard

Being a development team lead, the task of interviewing new candidates was part of my job.  Like any typical interview, we started with some easy questions to get them warmed up and help calm their nerves before hitting the hard stuff.

One of those easier questions was almost always: “Name some benefits of object-oriented development.”  Nearly every time, the candidate would chime in with a plethora of canned answers which typically included: “it helps ease code reuse.”  Of course, this is a gross oversimplification.  Tools only ease reuse, its developers that ultimately can cause code to be reusable or not, regardless of the language or methodology.

But it did get me thinking…  we always used to say that as part of our mantra as to why Object-Oriented Programming was so great.  With polymorphism, inheritance, encapsulation, etc. we in essence set up the concepts to help facilitate reuse as much as possible.  And yes, as a developer now of many years, I unquestionably held that belief for ages before it really struck me how my views on reuse have jaded over the years.  In fact, in many ways Agile rightly eschews reuse as taking a backseat to developing what's needed for the here and now.  It used to be I was in complete opposition to that view, but more and more I've come to see the logic in it.  Too many times I've seen developers (myself included) get lost in design paralysis trying to come up with the perfect abstraction that would stand all time.  Nearly without fail, all of these pieces of code become obsolete in a matter of months or years.

It’s not that I don’t like reuse – it’s just that reuse is hard.  In fact, reuse is DAMN hard.  Many times it is just a distraction that eats up architect and developer time, and worse yet can be counter-productive and force wrong decisions.  Now don’t get me wrong, I love the idea of reusable code when it makes sense.  These are in the few cases where you are designing something that is inherently reusable.  The problem is, most business-class code is inherently unfit for reuse!

Furthermore, the code that is reusable will often fail to be reused if you don’t have the proper framework in place for effective reuse that includes standardized versioning, building, releasing, and documenting the components.  That should always be standard across the board when promoting reusable code.  All of this is hard, and it should only be done when you have code that is truly reusable or you will be exerting a large amount of development effort for very little bang for your buck.

But my goal here is not to get into how to reuse (that is a topic unto itself) but what should be reused.  First, let’s look at an extension method.  There’s many times where I want to kick off a thread to handle a task, then when I want to reign that thread in of course I want to do a Join on it.  But what if I only want to wait a limited amount of time and then Abort?  Well, I could of course write that logic out by hand each time, but it seemed like a great extension method:

   1: public static class ThreadExtensions
   2: {
   3:     public static bool JoinOrAbort(this Thread thread, TimeSpan timeToWait)
   4:     {
   5:         bool isJoined = false; 
   6:  
   7:         if (thread != null)
   8:         {
   9:             isJoined = thread.Join(timeToWait); 
  10:  
  11:             if (!isJoined)
  12:             {
  13:                 thread.Abort();
  14:             }
  15:         } 
  16:         return isJoined;
  17:     }
  18: } 
  19:  

When I look at this code, I can immediately see things that jump out at me as reasons why this code is very reusable.  Some of them are standard OO principles, and some are kind-of home grown litmus tests:

  • Single Responsibility Principle (SRP) – The only reason this extension method need change is if the Thread class itself changes (one responsibility).
  • Stable Dependencies Principle (SDP) – This method only depends on classes that are more stable than it is (System.Threading.Thread), and in itself is very stable, hence other classes may safely depend on it. It is also not dependent on any business domain, and thus isn't subject to changes as the business itself changes.
  • Open-Closed Principle (OCP) – This class is inherently closed to change.
  • Small and Stable Problem Domain – This method only cares about System.Threading.Thread.
  • All-or-None Usage – A user of a reusable class should want the functionality of that class, not parts of that functionality.  That’s not to say they most use every method, but they shouldn’t be using a method just to get half of its result.
  • Cost of Reuse vs. Cost to Recreate – since this class is highly stable and minimally complex, we can offer it up for reuse very cheaply by promoting it as “ready-to-go” and already unit tested (important!) and available through a standard release cycle (very important!).

Okay, all seems good there, now lets look at an entity and DAO.  I don’t know about you all, but there have been times I’ve been in organizations that get the grand idea that all DAOs and entities should be standardized and shared.  While this may work for small or static organizations, it’s near ludicrous for anything large or volatile.

   1: namespace Shared.Entities
   2: {
   3:        public class Account
   4:        {
   5:                public int Id { get; set; } 
   6:  
   7:                public string Name { get; set; } 
   8:  
   9:                public Address HomeAddress { get; set; } 
  10:  
  11:                public int Age { get; set;} 
  12:  
  13:                public DateTime LastUsed { get; set; } 
  14:  
  15:                // etc, etc, etc...
  16:        }
  17: } 
  18:  
  19: ... 
  20:  
  21: namespace Shared.DataAccess
  22: {
  23:        public class AccountDao
  24:        {
  25:                public Account FindAccount(int id)
  26:                {
  27:                        // dao logic to query and return account
  28:                } 
  29:  
  30:                ... 
  31:  
  32:         } 
  33: }


Now to be fair, I’m not saying there doesn’t exist an organization where some entites may be extremely static and unchanging.  But at best such entities and DAOs will be problematic cases of reuse.  Let’s examine those same tests:

  • Single Responsibility Principle (SRP) – The reasons to change for these classes will be strongly dependent on what the definition of the account is which can change over time and may have multiple influences depending on the number of systems an account can cover.
  • Stable Dependencies Principle (SDP) – This method depends on the data model beneath itself which also is largely dependent on the business definition of an account which can be very inherently unstable.
  • Open-Closed Principle (OCP) – This class is not really closed for modification.  Every time the account definition may change, you’d need to modify this class.
  • Small and Stable Problem Domain – The definition of an account is inherently unstable and in fact may be very large.  What if you are designing a system that aggregates account information from several sources?
  • All-or-None Usage – What if your view of the account encompasses data from 3 different sources but you only care about one of those sources or one piece of data?  Should you have to take the hit of looking up all the other data?  On the other hand, should you have ten different methods returning portions of data in chunks people tend to ask for?  Neither is really a great solution.
  • Cost of Reuse vs. Cost to Recreate – DAOs are really trivial to rewrite, and unless your definition of an account is EXTREMELY stable, the cost to promote, support, and release a reusable account entity and DAO are usually far higher than the cost to recreate as needed.

It’s no accident that my case for reuse was a utility class and my case for non-reuse was an entity/DAO.  In general, the smaller and more stable an abstraction is, the higher its level of reuse.  When I became the lead of the Shared Components Committee at my workplace, one of the original goals we looked at satisfying was to find (or create), version, release, and promote a shared library of common utility classes, frameworks, and data access objects.  Now, of course, many of you will point to nHibernate and Entity for the latter, but we were looking at larger, macro collections of data that span multiple data sources of varying types (databases, web services, etc).

As we got deeper and deeper in the details of how to manage and release these items, it quickly became apparent that while the case for reuse was typically a slam dunk for utilities and frameworks, the data access objects just didn’t “smell” right.  We ended up having session after session of design meetings to try and find the right way to share these data access components.

When someone asked me why it was taking so long to iron out the shared entities, my response was quite simple, “Reuse is hard...”  And that’s when I realized, that while reuse is an awesome goal and we should strive to make code maintainable, often times you end up creating far more work for yourself than necessary by trying to force code to be reusable that inherently isn’t.

Think about classes the times you’ve worked in a company where in the design session people fight over the best way to implement a class to make it maximally reusable, extensible, and any other buzzwordable.  Then think about how quickly that design became obsolete.  Many times I set out to do a project and think, “yes, this is the best design, I can extend it easily!” only to find out the business requirements change COMPLETELY in such a way that the design is rendered invalid.  Code, in general, tends to rust and age over time.  As such, writing reusable code can often be difficult and many times ends up being a futile exercise and worse yet, sometimes makes the code harder to maintain because it obfuscates the design in the name of extensibility or reusability.

So what do I think are reusable components?

  • Generic Utility classes – these tend to be small classes that assist in a task and have no business context whatsoever.
  • Implementation Abstraction Frameworks – home-grown frameworks that try to isolate changes to third party products you may be depending on (like writing a messaging abstraction layer for publishing/subscribing that is independent of whether you use JMS, MSMQ, etc).
  • Simplification and Uniformity Frameworks – To some extent this is similar to an abstraction framework, but there may be one chosen provider but a development shop mandate to perform certain complex items in a certain way.  Or, perhaps to simplify and dumb-down a complex task for the average developer (such as implementing a particular development-shop’s method of encryption).

And what are less reusable?

  • Application and Business Layers – tend to fluctuate a lot as requirements change and new features are added, so tend to be an unstable dependency.  May be reused across applications but also very volatile.
  • Entities and Data Access Layers – these tend to be tuned to the scope of the application, so reusing them can be hard unless the abstract is very stable.

So what’s the big lesson?  Reuse is hard.  In fact it’s damn hard.  And much of the time I’m not convinced we should focus too hard on it.

If you’re designing a utility or framework, then by all means design it for reuse.  But you most also really set down a good versioning, release, and documentation process to maximize your chances.  For anything else, design it to be maintainable and extendable, but don’t waste the effort on reusability for something that most likely will be obsolete in a year or two anyway.

Print | posted on Thursday, May 27, 2010 8:48 PM | Filed Under [ My Blog Software ]

Feedback

Gravatar

# re: Code Reuse is (Damn) Hard

White Text On Black Background is (Damn) Hard.

Otherwise, very nice post.

Eddy.
5/28/2010 2:59 AM | Eddy Young
Gravatar

# re: Code Reuse is (Damn) Hard

Good post. I like the message that while reuse is a laudable goal, when you step back and look at what you're doing it can sometimes be the case that reuse is not applicable or rewarding in your specific context.

Ultimately, I think this emphasises the need for spending time identifying your goals/constraints and not just applying every known practise in the book, missing a deadline and eventually delivering something nobody wants.
5/28/2010 4:52 AM | Martin Rue
Gravatar

# re: Code Reuse is (Damn) Hard

@Eddy: Hah, yes it is... I've been meaning to switch up my theme (this is one of the canned GWB ones), just haven't gotten around to it yet.

@Martin: Agree totally. If you're writing a business application for a specific purpose, reuse will be low and a distraction. If you're writing a framework component, reuse will be much higer and time taken to maximize the design for reuse should be accounted for in the project plan (because it's always more time than you think...)
5/28/2010 8:28 AM | James Michael Hare
Gravatar

# re: Code Reuse is (Damn) Hard

I agree white on black is (damn) hard to read!

Thanks for the valuable post! I learned from it!
5/31/2010 7:42 AM | bushfoot
Gravatar

# re: Code Reuse is (Damn) Hard

Well, updated my teme now... better?
6/3/2010 10:45 AM | James Michael Hare
Gravatar

# re: Code Reuse is (Damn) Hard

New theme looks good. Great post as well.
6/3/2010 9:05 PM | Dan Martin
Gravatar

# re: Code Reuse is (Damn) Hard

thanx for all
6/3/2010 10:46 PM | توبيكات
Gravatar

# re: Code Reuse is (Damn) Hard

Lots of interesting observations here. I agree, it's hard. One usually ends up with a new product, which are all the reusable componentes, that needs to be maintained and shipped in new and improved versions. So it basically adds up a lot of extra work for an organization.
9/19/2010 9:46 AM | Oddleif
Gravatar

# re: Code Reuse is (Damn) Hard

Interesting points raised in this post, thank you.
There are a lot of "reusable things" that can actually be treasured in every project. Some of them can be coded to be reused, possibly during the project debriefing, others can't be coded, as particular adjustments to well-known architectures/frameworks and design patterns. Well, they can be coded but maybe not as you would with a component, they can be anyway saved for later use. Sometimes, I'm a bit worried about the effort that seems to go into code reuse which could actually end up being a missed opportunity to learn other important things that might not necessarily fall into any pre-existent category.

Ivy
1/10/2011 9:39 PM | Ivy
Gravatar

# re: Code Reuse is (Damn) Hard

@Ivy:

True. A lot of times you have companies that develop extensive class libraries by their senior developers, leaving nothing but grunt work and proprietary library usage for junior developers, which can be dull and, as you said, make them miss out on a good experience.

I've gotten a lot more pragmatic as i've matured as a developer. If a class is completely generic (a pure utility), then I'll create a shared assembly for it, but if there's any chance its applicability is limited (or the view of it is limited) to just one particular application, I usually avoid reuse initially until it has time to mature.
1/13/2011 6:33 PM | James Michael Hare
Gravatar

# re: Code Reuse is (Damn) Hard

Great post.

I am an "enthusiast" developer, learning this stuff on my own, as I go. Totally agree that designing for reuse is quite difficult. I have also found that instead of focusing on re-use during development (to me, it's always good to keep in mind, but not to the exclusion of getting a program working), I allow patterns for potential re-use to emerge as the application grows and/or refactoring/refinement takes place.

Of course, most of my development occurs either in a mid-sized "enterprise" context where I am simultaneously architect (so-to-speak) lead developer, and sole coder, so my experience is limited, and includes virtually no team POV.

I have found it is often easier to adapt a concrete implementation later, when re-use is required, than it is to modify existing "ready-for-re-use" abstractions down the road as requirements change.

The exception for me are those pre-defined application boundaries where the requirements clearly indicate potential for multiple uses.
3/9/2013 9:47 AM | John Atten
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: