9

When posed with a problem, particularly when it is complicated in nature, I try to take some time to think about the approach I am going to take to solve the problem. Despite this, what often happens is, as I am programming the solution, I start to think of details of the problem that I missed, and I adjust the code accordingly.

What results is a mess of code that needs to be refactored.

I want to "refactor as I go," but while it sounds easy enough to do, I have a really hard time doing it. When the detail that I missed is small, it is tempting to make a small update to my design, rather than erase what I've already written and write it the way it is supposed to be.

It sounds like a question with an obvious answer, but are there any techniques to use to better "refactor as you go"? I know that this is a good principle, but I fail with it time and time again.

7 Answers 7

17

Probably the fundamental observation in Fowler's Refactoring book is that you are much better off separating changes that affect code behavior from changes that don't. Those in the latter category, we call "refactoring". If you mix these changes, you mess them up - so don't. You can switch back and forth between coding and refactoring modes, but don't do both at the same time. Because if you try to, you won't know what you did wrong.

If you try to introduce a behavior-modifying change, and the behavior doesn't modify: you did something wrong; review it and fix it. Maybe just roll it back.

If you try to introduce a refactoring, and the behavior does modify: you did something wrong; review it and fix it. Maybe just roll it back.

You can't make that simple assessment if you attempt both changes at once. Nor can you, if you don't have unit tests. Write them. And do one thing at a time.

3
  • 1
    Interesting advice... Though, as all advices, not 100%. I think this advice works well if you do relatively small refactoring of a big code. But sometimes actually radical refactorings, like almost complete rewrite, are also accompanied with brand new features - and it is efficient to do both at the same time.
    – Tomas
    Apr 3, 2012 at 0:04
  • 3
    @Tomas: The question was clearly about "refactoring as you go", as was Carl's answer (+1 from me). An "almost complete rewrite" is not refactoring... it's, well, a rewrite. Apr 3, 2012 at 0:38
  • 1
    Not only mix coding with refactoring will mess things up, but also doing different refactoring at the same time will.
    – Rangi Lin
    Apr 4, 2012 at 16:51
6

Here's what I often do: leave yourself notes as comments, or even commented-out code. When you've got the sloppy code working, you can now re-organize it easily.

Also, there's nothing inherently wrong with refactoring as you go, just make sure you have ways to test that your refactored code doesn't introduce new bugs before you proceed.

3
  • agreed. It is often easier to refine ugly working code into beautiful code than trying to do it from scratch. You just have to be honest and give yourself the time to clean your solution once it is working. Apr 2, 2012 at 23:29
  • 2
    Beware of leaving too much commented-out code though, or else it will clutter everything up. In general if you've replaced bad code, you should just delete it. Also if it's a group project, your fellow coders will generally dislike you for committing commented-out code without an excellent reason.
    – user24795
    Apr 2, 2012 at 23:49
  • 1
    My approach: if you're not certain of what you're doing, comment out the old code. Include your initials and the date. If you come across stuff that no longer seems relevant with an old date, delete it, particularly if it has your initials on it. If you reactivate old code and do not know why it was deactiviated, add a thorough explanation so when someone (you, perhaps) later wants to put it back the way it was, you'll know you're in an infinite development loop and know how to get out of it. Apr 3, 2012 at 16:40
5

Another good rule of thumb is that every method should do one thing only.

If you break down the processing of what you are trying to implement into small discrete steps, and code the steps as methods, then when it is time to re-factor, you will find that the only refactoring that makes any sense is to change the order that the methods are called in or to leave some methods out of the execution path.

Also, since your methods are discrete, you are free to re-factor any single method as long as the value returned is what it should be, none of the other code will be any the wiser that a change was made.

This sets the stage for you to perform incremental improvements to your code over time by simply optimizing a method here and there. The overall logic doesn't change, just the details of a particular step.

If you follow this up from methods to classes, where in your classes do or represent only a single object, you will find that, just as with the methods, your refactoring becomes more just a reshuffling of the order that your classes are constructed and used, and less about actually changing the detail logic.

You know you have hit the sweet spot when the program specification changes drastically and suddenly, but you are able to accomodate nearly all of the required changes simply by modifying the relationships between your objects - without changing their internal implementation at all! It feels like you suddenly acquire nirvana!

Good luck and may all your code be bug free!

Rodney

3

Unit testing the code might help.

0
2

Is it possible that you're writing interfaces as if taking a top-down approach to solving a problem, but then implementing from the bottom up? If so then I think that's the logical mismatch that is causing you problems.

Based on what you're describing it might be better to break the problem down so that you can think of at least one thing you need to do, then implement just that. Think of another thing you need to do and implement just that. Keep on going, building your solution so that you write the absolute fundamentals first, then the building blocks that rest on top of those and so on upward until you've got the actual solution nailed down. I tend to find that the complete solution, even to hard problems, tends to sneak up on you and suddenly jump out when following that sort of approach.

If you keep the intermediate building blocks logically focussed and deliberately keep each segment of code closed and limited then the amount of refactoring you'll need to do should always be small and isolated.

1
  • This is what I am doing, I think. I often write the outline of what I want to do - like in the template method design pattern, for example. Then I implement those methods. When I get down to the details, sometimes I find out something wasn't right in the original template.
    – Kirby
    Apr 3, 2012 at 0:36
1

... it is tempting to make a small update to my design, rather than erase what I've already written and write it the way it is supposed to be.

Nothing is supposed. The only supposed thing is that:

  1. you can do any thing you want
  2. your lifetime / worktime is limited and you can only reach a limited number of things.

What do you want? One finished absolutely perfect super-duper-excellent-clean-code project, or three finished projects, only 70%-code-elegancy compared to the former, but 3 times 95% of the from the user's point of view? What will be more useful for the world, for your people, for your mission?

What do you want?

Of course, in long term, it might pay off to refactor (lowers mtce time etc.), but only in long time. We, programmers, are often tempted to refactor and optimize much sooner than it is needed. We often refactor a code that is not further developed, which means wasted time.

I use my own golden rule of 2-3. In the first usage of particular code, I just bastle it ASAP. It almost never pays off to think about more general usage of it, because you don't have enough information yet on what use-cases might come. And also, this part of code might be never used again. When the code is needed second time, I try to make the first code more general to apply to this case also, or I choose to copy it (but this I don't like). With the 3rd usage I feel I have quite sufficient information to refactor and, as this part of code seems to be really used, I feel it pays off.

0

What results is a mess of code that needs to be refactored.

Just blow through the problem until you understand it. Then make a fresh, correct implementation after you know its form.

To answer your question in more general terms: Remind yourself that necessary changes generally cause less ripple/retesting if made sooner than later.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.