Constant Refactoring Isa Good Thing

Here's a thought experiment. Suppose restructuring our program (we call it refactoring) was very very easy. Suppose for a moment that it cost nothing.

Then imagine that we somehow knew the entire ultimate design for every object, and we implemented each object just the way the ultimate design said it wanted to wind up.

We implement the first object (pick any one) fully. Then we implement the second, which depends on the first. All the time we are implementing the second one, there is functionally in the first that isn't being fully used in the app. As we begin to implement the third, ditto: there is functionality in the first and second that isn't really used [yet] in the app. Now, it is going to be used, someday it will be fully used, but it isn't yet.

Finally we begin to implement the hundredth object, and finally the application begins to do something the customers actually want. Pretty soon, part way through that 100th object, it does something they want badly enough that they are willing to pay for it. It provides value.

Note that this is the very first moment that we've done something they actually like. All the time up till now, all the code still in the system and unused, is wasted in terms of bringing value to the user.

So, obviously this is a bad way to do things. On our next project, what we do is get the perfect design, but implement it incrementally, in a way that brings value to the customer as quickly as possible. Then we go back and add a bit of functionality to each object to bring it up to the next level of user value. Repeat again and again.

This is much more effective in terms of providing value. There is a really good chance that the users will actually deploy something long before we're totally done. (The C3 users deployed the entire payroll a year ago, and we aren't done yet.)

But wait, we still are losing money! All that design that we did up front is still wasted, because it is just waiting around to be put into the system. We could actually deliver more value to the user sooner (thus more cheaply) if we just did the part of the design we needed, and left the rest until we needed it.

But how can we know when we design that first object what part of the design will support the part of the system that we'll do first. Well, in practice, usually we can't tell in the DesignPhase: that knowledge has to be driven from immediate requirements, not full requirements.

So we go Extreme. We focus on user functionality (in the context of a carefully- but quickly-chosen architecture). We let that functionality "tell us" what the supporting objects need to be, and just implement the bits that are needed to get the user value.

Doesn't this mean we get it wrong a lot? Yes, we get almost every inside object wrong at least in the sense that it probably will need more methods a year from now than it has now. Sometimes we get it conceptually wrong altogether.

But that doesn't matter, because our original hypothesis is: refactoring is very very easy, and costs almost nothing.

The result is delivery of user-valued function at the absolute first possible moment, with never a moment wasted.

Now, of course refactoring isn't quite free, and we aren't quite that good. The amazing thing to me in doing XP this past two and a half years is how close you can get. We really haven't much felt that we had suffered by not thinking more sooner or building some object stronger sooner. Most of the places where we have that sensation at all come from needs that weren't identified up front anyway (and remember, C3 had a number of years of planning and requirements and thrown-away design up front).

So ... when we get into the fantasy that refactoring isn't hard ... maybe we can see that design-as-you-go can be a very effective way to proceed. Thing is ... refactoring isn't that hard in any object language ... and it's very very easy in Smalltalk.

-- RonJeffries


Q and A


An EngineeringTask is done when the UnitTests for it run. A story is done when the AcceptanceTests for it run. Refactoring is changing code without changing functionality, so it never makes something not done that used to be done.

See ExtremePlanning for details of how we plan: story to tasks to tests to cleaning up the code to done. IterationPlan plans three weeks at a time; CommitmentSchedule plans everything about the next release, multiple iterations. Engineers sign up for and estimate tasks and we track how they perform against estimates.

We use a constant LoadFactor to account for time spent partnering, refactoring, attending meetings, etc. The LoadFactor seems to stay pretty constant: one time it changed we determined that we hadn't been doing enough refactoring and the code was getting hard to work in. Another time it was due to lack of AcceptanceTests, which made it hard to know when we were done. We fixed these things and LoadFactor improved.

Remember, we're not talking about not doing something that is needed, we're talking about doing everything that is needed, only when it is needed. We do as much (probably more) design as any other approach: it's JustInTimeDesign.


CategoryRefactoring


EditText of this page (last edited October 29, 2011) or FindPage with title or text search