CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

Why do we Refactor?

I've been refactoring the StructureMap codebase in preparation for wrapping up the 2.5 release.  I've been doing this work with a couple goals in mind.

  1. Experimentation.  I'm purposely revisiting some code just to see if I can come up with a better solution and structure.
  2. Improving the structural quality of the code in order to make the code more approachable to others.
  3. Some of the code is flat out embarrassing.
  4. Changing the structure to allow for easier extensions.  There's a couple specific new pieces of functionality that I thought would be easier to implement if I did some refactoring first.
  5. To more closely align the architecture with its functionality and usage.  StructureMap was originally envisioned as a generic mechanism to build an object graph from some sort of Xml representation.  Today, we do vastly more things with an IoC tool than I had in mind back in the summer of 2003.

 

As the effort proceeds, I've been thinking about the reasons that we do refactoring, or more importantly, the reasons that justify refactoring.  When the topic of refactoring as a described practice was first being broached earlier in this decade, I read a lot of people upset over the idea of refactoring.  Refactoring was variously described as:

  • A result of insufficient upfront design (which I still think is silly because refactoring is often a result of doing upfront design too early and/or getting that upfront design wrong)
  • Undisciplined hacking
  • A dangerous activity that needlessly risked destabilizing working code (a very real fear that can nevertheless be mitigated by good automated test coverage with well written code and well written tests)
  • A waste of resources.  Useless goldplating, or as my buddy Chad Myers would say, "polishing the nosecone."

That being said, what are the responsible reasons that lead us to refactor our code?  I can think of two major reasons.

  1. To remedy a deficiency in code, design, or architectural quality.
    1. We shouldn't declare a coding task complete until it reaches the quality demanded by the team.  These refactoring's are generally small, cheap items like renaming methods and extracting methods that can be performed safely and quickly with an automated tool.
    2. I'm doing a lot of work on StructureMap as a result of static analysis results from NDepend.  NDepend helpfully pointed out some flaws in the class structure that I've since remedied.  In specific, I've been working on reducing the complexity of any class with a high Cyclomatic Complexity (all the classes are now under 30 with a few exceptions that I'm ignoring because the methods are all simple).  I would also worry about efferent and afferent coupling plus the size of the classes and methods in your system. 
  2. To make a forthcoming change to the code easier. 
    1. If I'm changing a big method or complex class I'll often start by refactoring towards a Composed Method by extracting methods or even smaller, more focused classes.  On a project last year I did some quick renaming of variables from "m0, m1, m3" to more descriptive names.  My goal in these cases is to simply make the existing code easier to understand *before* I start to make modifications.
    2. For a new feature, I might need part of the functionality of an existing method or a class, but not the rest of it.  Rather than duplicate that functionality by copying and pasting, I'll extract the functionality to be shared into a new class or method so that it can be easily reused by the code for the new feature.
    3. In several cases, I've wanted to change one aspect of the behavior of an existing class while leaving the rest of the class's behavior intact.  In that case, I think the class is violating the Single Responsibility Principle, and I've split the class up along responsibility lines to isolate the changes to a smaller surface area. 
    4. Before making a change, you may need to decrease or even eliminate coupling on an existing code structure.  In StructureMap, I've frequently found myself wanting to change the internal structure of a class for various reasons, but first being blocked because other classes are coupled to internal details of the existing code structure.  In many cases, the culprit has been a Law of Demeter violation. 
    5. Introducing new abstractions often gives us a new seam in the class structure that we can exploit to add new behavior with minimal change to existing classes
    6. If we have to change any functionality that has duplicated representations in the code, it's often worth the while to first refactor the code to eliminate that duplication before making the change.

 

Either way, you're main goal with refactoring is to make the code easier to work with in the future.  In some cases (the second category), that desired change is immediate.  In the first category, you're simply keeping the code cleaner to handle any type of extension and make the system easier to understand.  In regards to Lean thinking, you only refactor on a project when the proposed refactoring will lead to better throughput of the development work.  If a structural problem in the code isn't causing any immediate harm or friction, you probably leave it alone for now.

 

Any thoughts?  What did I miss?



Comments

jdn said:

"Some of the code is flat out embarrassing."

Oh, yeah.  Feeling that on something right now.

Part of getting better as a dev, but still.  I did *what*?

# May 15, 2008 9:50 PM

Chad Myers said:

For the record, I don't think all refactoring is polishing the nosecone. I use that term to describe when I think I'm going overboard or spending too much time on a refactoring with limited return on investment :)

# May 16, 2008 2:29 AM

Steve Gentile said:

ditto jdn, when I saw that line, it made me feel better  :)

refactoring is a great thing, I typically spend designated time to stop rolling the code and step back and refactor.  

Mostly I concentrate on readability and maintainability - keeping things simple (which is difficult).

# May 16, 2008 7:42 AM

Dew Drop - May 16, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - May 16, 2008 | Alvin Ashcraft's Morning Dew

# May 16, 2008 8:37 AM

Derick Bailey said:

I'd like to hear more about how your Law of Demeter violations were causing problems, and why the removal of those issues helped you restructure the code.

... as a tangent to that; what are you're thoughts on Fluent Interfaces in the context of LoD?

# May 16, 2008 10:37 AM

Code Monkey Labs said:

General Disabling the Visual Studio Service Host : A new feature in Visual Studio 2008 is the WCF service host - similar to the web development server, but for WCF services. At times, this is handy...all other times, it's obnoxious. Nicholas Allen has

# May 16, 2008 8:06 PM

The Inquisitive Coder » Blog Archive » Weekly Links 4 said:

Pingback from  The Inquisitive Coder  » Blog Archive   » Weekly Links 4

# May 17, 2008 7:25 AM

Shawn Oster said:

I often refactor as a function of scale.  When enough new features have been added the code starts wafting this stench that code is being duplicated.  In a toss away app this isn't too much of an issue but anything that's going to require maintenance by others needs to be refactored as a sanity check.  Nothing worse than thinking you've fixed an issue only to find the exact same code somewhere else tripping you up.

The other reason I'll refactor is it gives me a chance to put my current problem into my subconscious for a bit of stewing.  With good source control and tests refactoring is rarely that strenuous nor does it require the same brainwork that figuring out a new feature does so it gives me a break while still doing something useful.

# May 18, 2008 4:30 PM

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# May 19, 2008 8:57 AM

Jeremy D. Miller said:

@Derick,

I'll get to it soon.  Think on this code though,

Plugin plugin = family.Plugins.Add(typeof(ISomething));

What if I want the PluginCollection returned by PluginFamily.Plugins {get;} to die?  It's arguably just a detail of PluginFamily, so I should be able to do that, except there's a hundred other classes that "know" about the Plugins property.  Violating LoD is a bit like wearing your underwear on the outside.

# May 19, 2008 3:27 PM

Ultram. said:

Ultram. Ultram tramadol. Generic ultram.

# June 20, 2008 7:06 AM

tree removal service said:

salute for trees!

# July 3, 2008 6:35 PM

tree removal California said:

please do care for our trees.

# July 3, 2008 6:36 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Jeremy D. Miller

Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy previously worked as a systems architect building mission critical supply chain software for a Fortune 100 company and learned agile development practices as a .Net consultant at ThoughtWorks, one of the pioneers of agile development. Jeremy is the author of the open source StructureMap (http://structuremap.sourceforge.net) tool for Dependency Injection with .Net and the forthcoming StoryTeller (http://storyteller.tigris.org) tool for supercharged FIT testing in .Net. Jeremy's thoughts on just about everything software related can be found on his weblog "The Shade Tree Developer" at http://codebetter.com/blogs/jeremy.miller, part of the popular CodeBetter site. Jeremy is a Microsoft MVP for C#. Check out Devlicio.us!

This Blog

Syndication

News

All opinions expressed here constitute my (Jeremy D. Miller's) personal opinion, and do not necessarily represent the opinion of any other organization or person, including (but not limited to) my fellow employees, my employer, its clients or their agents.

About Me

"Best Of" Compendium

StructureMap (Dependency Injection for .Net)

StoryTeller (Supercharged Fit)

Build your own Cab

TestDriven

MVP