Refactoring - why some software changes are easy, others are hard

(From an email I sent to one of my clients regarding our software project.)

Last week we left the meeting with an open item to have [CUSTOMER_NAME] IT staff work w/ [DEVELOPER_NAME] to understand why some programming changes are easy, and some are not. More specifically, I think the question pertained to times when an application works one way in one part of the application, and then a user would like to see that same behavior in another part of the application. The question was something to the effect of "Why isn't this easy?"

In relative order of difficulty, here are some reasons I thought about this weekend.

(1) If the two classes share the same class ancestry, and a behavior is available in one class, but not in the second class, the proper thing to do is to push the behavior up the class ancestry to a common superclass, and then make whatever changes need to be made to accomodate the second class. Also, because the method is now available to all child classes inheriting behavior from the superclass, you may have to change every child class to account for the new, inherited behavior. So, you need to change the originating class, the superclass, and all child classes, then test.

(2) A second possibility is that two classes do not share a common ancestry. This can happen when a developer sees two tools as having so many separate behaviors that they aren't really alike, but then as requirements change the tools start to become more alike. In this case the behavior in the first class can't be pushed up to a common superclass. You can do something really bad here, like copy and paste the code from one class to the other. Unless there is an incredible emergency a developer should never do this. This has been done a lot in older programs, and starts to snowball into an unmanageable mess of spaghetti code, which eventually makes an application impossible to maintain. Things a developer can and should do here are to consider that maybe these two classes should have a common ancestor, and then push the desired behavior up the family tree; or, extract the desired behavior to a common third class. To me this is really a situation-dependent answer. Finally, test.

(3) A complexity that can affect either (1) or (2) above occurs if the second class in our example does not have access to the same resources that the first class had. As a simple example, if the second tool does not have access to an object named HelperClass, and HelperClass was already created and populated with needed information somewhere else, that instance of the HelperClass needs to somehow be made available to the second class. This can involve significant rewiring of the program, including changing the call stack to the instance of the second class. You have to find out where the HelperClass was instantiated, and then find the correct way to get it to your second class. Sometimes this is easy, sometimes it is not. As mentioned, this involves changing call stacks in the application, as well as changes to the signature of the second class. Finally, test that it all still works.

Regarding these three items, more is written about this in Martin Fowler's classic book titled "Refactoring". It costs about $45, and we have several copies at our office. IMHO, if you don't own it, buy it.

Finally, the "spaghetti code" principle really needs to be watched here. It's very easy to make quick "fixes" to an application to try to get behavior to a user faster. However, sooner or later all this duct tape and bondo comes to a horrible collapse, and you end up with an unmaintable mess of code. When this happens, nobody can write any new code because (a) nobody can understand how it works and (b) changing one part of an application has unforeseen disastrous effects on another part of the code, finally leading to (c) no developer wants to own the code or work on the project because it will make them look very bad no matter what they do.

That's the general answer to the question posed. For a more specific answer we'll have to have XXX or YYY sit down with a [CUSTOMER_NAME] developer to dig into more of the specifics.