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?

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. 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 is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • devMomentum

    Refactoring makes you feel good. Your code gets so much cleaner (if not leaner).

    With VS2008 we do not have any more reasons not to write small methods, we have the refacor tool one right-click away. It does everything for us. VS could warn us (with warnings for example) that a method exceeds some cyclomatic complexity threshold.

    One other aspect I think we refactoring is testability. If I have code that has some goal but I have no way of testing it easily, it is a refactor case. Sometimes just putting the code in the right place solves the thing.

    I think tools will help us identify rapidly what are the weak spots in our code.

  • http://www.jamesbrowntreeandlimbservice.com/ tree removal California

    please do care for our trees.

  • http://www.moralestreeservices.com tree removal service

    salute for trees!

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @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.

  • http://blog.enginefour.com Shawn Oster

    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.

  • http://www.avocadosoftware.com Derick Bailey

    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?

  • http://blogger.forgottenskies.com Steve Gentile

    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).

  • http://chadmyers.lostechies.com Chad Myers

    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 :)

  • http://www.blogcoward.com jdn

    “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*?