Refactoring Hell

Refactoring is generally hard to screw up. You just change one thing at a time.

Sometimes, however, you want to go faster, so you change many things at a time, attempting to perform some grand sweeping global optimization. Sometimes you can pull this off, but other times you cannot. The changes are holographic. You end up having to make changes throughout the entire system just to make it build again, let alone run.

When you finally get to the point where it runs, it no longer works. Moreover, you've given up trying to do a good job, instead resorting to hacking about just to get to a working state again. You are making things worse.

You have entered RefactoringHell.

There is only one way out. Delete everything. Start again at your last clean state. If you don't have version control, your last clean state is an empty disk.

The way not to get into RefactoringHell is to make one change at a time: each change is made OnceAndOnlyOnce; and no change is made because it might be nice to have the changed version later on, but only because you really do need the changed version right now: YouArentGoingToNeedIt.

In the last couple days, I've decided to reorder the dependencies between various subcomponents in our system in order to allow the linker a better chance to dump dead code as well as to prepare for the next two components in the system. Unfortunately, as it turns out, the changes are not subtle. The system isn't factored well enough to change the dependencies; indeed, the system is entirely dependent on the broken dependency ordering.

While this would normally not bother me, I spent a month doing ConfigurationManagement, in which time others took the "well-factored" system and built a ton of projects on top of it. Still, this wouldn't phase me normally, except I'm losing track of what goes where. I'm making bad decisions, making the design worse. This whole exercise was meant to reduce the compiled footprint, but I'm slowing growing it.

I'm hosed. I have nowhere to go but backwards. Delete my whole source tree and refresh it from source control.

The last time I did something this stupid was my last semester at school. I was writing an inference engine for my data mining course final project. In the last 12 hours I decided to add fuzzy logic. For a system that is essentially one giant logic equation resolver, adding fuzziness is non-trivial. It became like a virus, infecting one method at a time, until the entire class tree was fuzzified. And it still wasn't working! The relationships between instances needed fuzzification. Everything had to be touched.

These lessons don't have much to do with refactoring yet. Changing the system's functionality isn't refactoring, but it does show what happens when the system isn't factored well enough to handle it. You can't make a fundamental change, no matter how small you think it is, and then hope to make it fit through judicious refactoring. You will have to refactor the entire system just to accommodate the change, and you'll have to do it all at once just to get it back to working order. This is how you enter into RefactoringHell when adding system functionality.

It's just like the crystallization effect when you put your canoe on a supercooled lake. The water's liquid surface instantly freezes to ice now it you've disturbed it. All the water molecules reconfigure themselves almost instantly into the crystalline structure of ice. Similarly, just to accommodate your small change, the entire system has to reconfigure. You just don't have the advantage of Mother Nature on your side. All you have are your fingers and the puff of wheat between your ears.

While watching ice form around you instantly is an amazing experience, RefactoringHell is not. The answer is simple though: Don't refactor the system all at once. Just remove the change that you want to make and then adjust the system slowly, one step at a time, until you can make your change.

The real trick is knowing when you're in RefactoringHell. That's easy though. Just count the Coke cans piling up on your desk. -- SunirShah

Another good way to tell that you're in RefactoringHell is if you've been trying to get tests to pass, without success, for more than ten minutes. This approach at least lets you know when you're in RefactoringHell.

A more aggressive approach that's worked well for me is to back out if two attempts at getting tests to pass, that should have worked, didn't. And of course, you should be trying for a green bar after every change. This approach takes more discipline, but keeps me out of RefactoringHell. (When I use it!) -- JimLittle

I certainly can relate. I'm working on legacy code, and the last two days, I decided to do a few small necessary changes to the database all at once. Then I found adapting the program to the new schema took me unnecessarily long, because the stored procedures were not returning meaningful error messages, but only 'OK' or 'ERROR'. So I rewrote all of those. Needless to say, after that I had to start debugging... and now it's 6:30 in the morning. Thanks God, by now the system is stable again.

I would like to be able to say that I have learned once and for all from this incident. But, honestly, these "grand sweeping refactorings" are probably just too tempting, because they are so much fun if you can pull them off. -- FalkBruegmann

To be honest, I have always found large refactorings to be an indicator of something I (or someone else) missed before. A big refactoring almost always indicates a whole lot of small refactorings that didn't quite make it in because 'it just wasn't needed'. Getting an XP project going - as has been said many times before - needs very disciplined practices - and that means making all the small refactorings needed - and keeping your nose awake searching for bad smells. There is never an 'easy' way around this - you just have to bite the bullet, re-do your estimate because this is going to take a hell of a lot longer to get done than you originally thought, and don't do it again. -- AmrElssamadisy

I routinely make 6 or 7 changes at once and it usually works just fine. If you know your code well enough it needn't be a problem. My favorite sport is to be working on a live web site, used by a lot of people, and to save changes to 5 or so files at once and have the system not skip a beat. If changing unfamiliar code caution is best. But why is it people make so many mistakes on what is familiar?

My favorite sport is to be working on a live web site, used by a lot of people, and to save changes to 5 or so files at once and have the system not skip a beat.

This practice deserves to be honored by a distinctive name - something reminiscent of RussianRoulette, preferably.

-- Todd Derscheid

Perhaps the existing phrase CowboyCoding

In a PhillipJoseFarmer? novel, a centaur-like creature from a culture based on the Native American model tries to get at the protagonist across a buffalo stampede. The comment is made that he was looking to be named He Who Dances Across Ragin Buffalo To Kill Kickaha, when he stumbled and fell under the hooves of the stampede. This seems appropriately poetic and fatalistic.

I was just thinking I needed to turn EVEN MORE of my language into obscure Sci-Fi references. It's like a FabulousRiverboat? in my head. Not to mention that I'd have to gloss that term for 100% of the company and private individuals with whom I converse. That's just DoublePlusGood for everyone.

This all adds weight to my theory that DevelopersAreMasochists. -- DarrenHobbs
See also: IamWeak, SeedCrystal

View edit of March 20, 2006 or FindPage with title or text search