January 31, 2013

Unfortunately, this anti-pattern is too common. It doesn't hurt until it hurts and when it does hurt, it hurts a lot.

If you are developing frameworks do not provide superclasses that framework users must inherit to use your framework. Inheritance is the one of the tightest forms of coupling you can use in OO. When you force your users to inherit from you, you:

Make it nearly impossible for users to test their logic independently of your framework

Make migration away from your framework difficult, or impossible.

The testing issue is subtle, but very real. If you write tests as you develop you notice it immediately. When you inherit code from a framework, it is mixed with your logic. Often you are obliged to run that inherited code with the code that you really want to test, along with all of its dependencies, start up time, etc.

Migration, as well, is a very real issue. If you've written logic important to your domain, there is nothing preventing you from being able to use that logic with other technology - nothing except coupling.

Yes, users of frameworks can guard themselves from that coupling by making their own adapters, but why put them through that? Most of us hate vendor lock-in strategies in business. Framework Superclasses are a very direct form of vendor lock-in.

As a framework designer, you have many other choices: eventing, listeners, and object composition.

January 22, 2013

There are many reasons to extract methods in old legacy code. Sometimes you just see a small piece of cohesive code and you ask yourself whether you can name it. If you can, extracting that code to a method is often a decent thing to do.

In the following code, it's easy to see that the while loop could be extracted into a simple method that forms a vector from the lines that we read from a file. It's a decent extraction, but is it high value?

Let's look a little closer.

What is the code doing? Well, up until the point at which we are getting the system properties, we are creating a list of internet addresses (email addresses) from the contents of a file. Is the way that this is done important to a reader? Probably not. We just care that we get those addresses so that we can use them. Let's extract that method:

Okay, things are much better. Now, the code hides an implementation detail. It hides the mechanism that it uses for getting email addresses from a file. Can we go further? Yes, we can. We can do the extraction above in a way that hides the design decision of going to a file to get the email addresses. In the following code, we just don't care where the email addresses come from:

When I talk to people about these, I sometimes call them 'Parnas Extractions' after David Parnas. Back in 1972, he wrote On the Criteria to be used in decomposing systems into modules, the paper the more or less defined the concept of modularity. The key point is that when we use modularity in a system, we should use it to hide design decisions. Frankly, we can waffle all day about whether a particular decision is a design or an implementation decision, but in the end it doesn't matter. It's a continuum, and extracting getEmailAddresses with a filename argument a step along the same direction as extracting it without one, or moving toward having another object provide the email addresses - it delineates a higher value axis of change in the code.

When you are working in legacy code and feel like breaking up large chunks of code, favor Parnas Extractions. They move your code closer toward embodying a better design.

January 15, 2013

Every once in a while, I ask someone whether they refactor their code. The reaction I get is hard to describe. Often within the span of a few seconds, I see the inward smile that says "Of course, I do" followed by the sad expression that says "Not as much as I should." Many times, those two micro-reactions are followed by "Oh my God, I don't want to think about the state of my code right now", or "Are you picking on me? I'm doing the best that I can."

I think there is a reason why this happens. We haven't been very good about settling the place of refactoring in software development. As a result, the practice is usually sloppy -- hit or miss.

The advice that most people get about refactoring is to do it as you go. And, frankly, if everyone did that, the state of many code bases would be much better. The reality, though, is that developers often see it as a less important activity. It doesn't seem like it has any immediate gain, so it is easy to short-change it in the face of a heavy workload. The natural response to this would seem to be scheduling time to refractor, but traditionally many organizations are reluctant to do that because (again) it is hard to see the payoff. Most of us have a story about a "refactoring iteration" that went wrong -- at the end the organization was unclear what benefit they received from it and/or upset that they accepted a lull in feature delivery. Refactoring "stories" don't have a good reputation either -- the ROI is unclear and there's always this nagging feeling that they would not be necessary if we all practiced "refactor as you go."

So, refactoring is sloppy. What can be done?

Well, there is a good path forward, but it involves doing something counter-intuitive. We can introduce a "hand off" in development. Here's how it works. For every story/feature that you accept in planning imagine how you would implement it if the code was in a state where it would accept the feature easily. Then, create a task for that refactoring work and make sure it is done before the other tasks for the feature, and (here's the trick) it should not be done by the whomever is going to do a feature addition task on the same story.

Here's the rationale. Breaking out refactoring at the task level highlights it. It's easier to focus on refactoring as as team when it is seen as separate activity. Having an hand-off between people the people who are doing the refactoring and the people who are doing the feature addition forces a degree of scrutiny and communication that is hard to have otherwise.

Like anything else in process, this is medicine. It's not meant to be "the way that people do things for all time" but rather as a way to raise the visibility of refactoring within a team, or to make deliberate progress toward some longer term refactoring goals.