Circular Refactoring

Every now and then, I'll notice that I've spent an hour or two refactoring and wind up where I started. I make some local changes, look at the code, make some other changes in a nearby object, look at the code, make some further changes, ... and realize, after a while, that I'm back pretty close to where I started.

Does this happen to other people? Why does this happen? What in the code makes for a circular refactoring? Is there a particular structural error that leads to this scenario? Or are there problems where this just happens more often?

-- WilliamGrosso

What is it specifically that you are refactoring for? Is it the same goal/criteria each time, or do you change that as you go along? Are you mostly keeping the dependencies and relationships pretty much the same and just juggling and cutting and pasting around that?

I've found myself going around this loop when I can't keep the requirements in my head. I satisfy A, make a change to satisfy B, discover a forgotten property of A, discover a forgotten property of B, and so on. One can satisfy nearly contradictory constraints by following this path around a few times, though a better approach is to shed some requirements, especially those self-imposed.

It can be hard to let go of readability or simplicity as a requirement, especially when you think that is why you are refactoring. A funny quirk of these mind-size based requirements is that they are rarely satisfied by trying harder. -- WardCunningham

Ward, can you (or someone else) clarify/expand that last sentence? It's intriguing, but I don't think I fully understand it.

I'd like to try. We're putting forward these base (fundamental) heuristics (for example, simplify, make it readable, refactor towards these ends). These approaches work when you keep them in mind as you go about your day. However, as in many good things, if too much stock is put in the way, it eludes. The harder you try, the harder it gets. -- MattSimpson

This description seems to imply that the XP rules are at odds when the EssentialComplexity of the problem is higher than a pair can deal with (while adhering to the XP rules).

This sounds a lot like a physical system oscillating between two local minima. Apply a larger impulse (kick the code) to see if the system will make a larger excursion and discover a lower energy state. -- MichaelNygard

This happens a lot to me when I try to apply ZeroOneInfinity to 2 instances of something, especially in the bloatness of C#. I'm starting to see 2 as a special number now, and leaning toward ThreeStrikesAndYouRefactor. For example, you set the values of 2 GUI controls (given their names and data). Now you could stop with 2 lines of coded, but ZeroOneInfinity demands that you create a new method, taking a whole name/value pair system to support arbitrary setting of control values. But in C# this requires an insane amount of casting and swapping from collection to arraylist to object[], etc., since the built-in arrays and collections are lame and it's all static typing.

C# is so incredibly bad at casting, collection and array interroperability that I would strike out many universally accepted refactorings just for this one language. Not to mention the sheer ugliness and confusion of delegates. Basically, if you apply some refactorings, the result is so complex that it is no longer DoTheSimplestThingThatCouldPossiblyWork. I hear things get better with generics but it'll take a year before my company upgrades.

I don't normally loop. But I sometimes come full circle, from time to time. Not very often, but it can happen.

For example, I might eliminate code duplication by introducing an InternalIterator. But in Java, without true closures, one has to deal with the complexity and limitations of AnonymousInnerClasses. So I may decide that simplicity wins over duplication, and put it back the other way. Having learned my lesson, I'll be a bit more hesitant to try the same trick again.

I might try something else, as the code smell is still there. But I can run out of good ideas on how to fix one problem without creating too many others; so I may decide to leave it until later, when I may have a better idea, or when the surrounding system may have changed sufficiently to make fixing the original problem easier.

If you or your team find that you are refactoring the code in circles, then it's time to stop and have a talk about it: You need to come to an agreement on a relative ordering of priorities and/or a goal that you wish to progress towards. Otherwise, you will waste a lot of time and effort. -- JeffGrigg

[MergeMe with CanOnceAndOnlyOnceLoop ?]
See RefactoringImprovingTheDesignOfExistingCode, WhenToStopRefactoring

View edit of January 10, 2010 or FindPage with title or text search