When is it time to refactor code?

Tag: language-agnostic , refactoring Author: yseskx Date: 2010-10-06

On on hand:
1. You never get time to do it.
2. "Context switching" is mentally expensive (difficult to leave what you're doing in the middle of it).
3. It usually isn't an easy task.
4. There's always the fear you'll break something that's now working.

On the other:
1. Using that code is error-prone.
2. Over time you might realize that if you had refactored the code the first time you saw it, that would have saved you time on the long run.

So my question is - Practically - When do you decide it's time to refactor your code?

Thanks.

possible duplicate of When do you refactor code?
When the Vulgar Word to Tech Word ratio exceeds N, Where N is an experimentally determined value dependent on local culture and project difficulty.

Best Answer

One of the most common mistakes i see is people associating the word "Refactor" with "Big Change".

Refactoring code does not always have to be big. Even small changes such as changing a bool to a proper enum, or renaming a method to be closer to the actual function vs. the intent is refactoring your code. With the exception of the end of a milestone, I try to make at least a very small refactoring every single time I check in. And you'd be amazed at how quickly this makes a visible difference in the code.

Bigger changes do take bigger planning though. I try and schedule about 1/2 a day every two weeks during a normal development cycle to tackle a bigger refactoring change. This is enough time to make a substantial improvement to the code base. If the refactoring fails 1/2 a day is not that much of a loss. And it's rarely a total loss because even the failed refactoring will teach you something about your code.

Other Answer1

A couple of observations:

On on hand: 1. You never got time to do it.

If you treat re-factoring as something separate from coding (instead of an intrinsic part of coding decently), and if you can't manage time, then yeah, you'll never have time for it.

  1. "Context switching" is mentally expensive (difficult to leave what you're doing in the middle of it).

See previous point above. Refactoring is an active component of good coding practices. If you separate the two as if they were two different tasks, then 1) your coding practices need improvement/maturing, and 2) you will engage in severe context switching if your code is in a severe need of refactoring (again, code quality.)

  1. It's usually isn't an easy task.

Only if the code you produce is not amenable to refactoring. That is, code that is hard to refactor exhibits one or more of the following (list is not universally inclusive):

  1. High cyclomatic complexity,
  2. No single responsibility per class (or procedure),
  3. High coupling and/or poor low cohesion (aka poor LCOM metrics),
  4. poor structure
  5. Not following the SOLID principles.
  6. No adherence to the Law of Demeter when appropriate.
  7. Excessive adherence to the Law of Demeter when inappropriate.
  8. Programming against implementations instead of interfaces.
  1. There's always the fear you'll break something that's now working.

Testing? Verification? Analysis? Any of these before being checked into source control (and certainly before being delivered to the users)?

On the other: 1. Using that code is error-prone.

Only if it has never tested/verified and/or if there is no clear understanding of the conditions and usage patterns under which the potentially error-prone code operates acceptably.

  1. Over time you might realize that if you would have refactored the code the first time you saw it - That would have save you time on the long run.

That realization should not occur over time. Good engineering and work ethics calls for that realization to occur when the artifact (being hardware or software) is in the making.

So my question is - Practically - When do you decide it's time to refactor your code?

Practically, when I'm coding; I detect an area that needs improvement (or something that needs correction after a change on requirements or expectations); and I get an opportunity to improve it without sacrificing a deadline. If I cannot re-factor at that moment, I simply document the perceived defect and create a workable, realistic plan to revisit the artifact for refactoring.

In real life, there will be moments that we'll code some ugly kludge just to get things running, or because we are drained and tired or whatever. It's reality. Our job is to make sure that those incidents do not pile up and remain unattended. And the key to this is to refactor as you code, keep the code simple and with a good, simple and elegant structure. And by "elegant" I don't mean "smart-ass" or esoteric, but that displays what is typically considered readable, simple, composable attributes (and mathematical attributes when they apply practically.)

Good code lends itself to refactoring; it displays good metrics; its structure resembles both computer science function composition and mathematical function composition; it has a clear responsibility; it makes its invariants, pre and post-conditions evident; and so on and so on.

Hope it helps.

comments:

+1 for the comprehensive reply, but the code as it is now, doesn't account for most of the standards you mentioned, and I really don't have time (-: (I'm not the one who wrote it). I meant to ask something like - when does it become so unbearable, that you decide you must refactor it, albeit time (and other) constrains.
@Oren - Ouch, you seem to be on a tight corner. It becomes unbearable when you keep spending enormous amount of times trying to implement a change correctly and you also spend an enormous amount of time trying to determine if the change won't break something else. Refactoring is not a viable option if degradation has occurred beyond recoverability. Sadly, when unchecked, the 2nd law of thermodynamics (entropy) also applies to software. When that happens we must bear it, rewrite it whole or leave to another job (because doing that kind of crap long-term is not conductive to one's career.)
@Oren - Ideally, cost of changes should be predictable and more or less proportional to the size of the change. On average, a uniform requirement change should trigger a code change of a predictable size (and thus cost.) Every once in a while a req. change might trigger a very large code change (or a change size that is not easily predictable.) And that's fine. When you can never reliably predict the cost of a change (in terms of a predictable and proportional code change) then things become unbearable... because you never fully know what you are getting into with each change request :/
Very nice response. As the Pragmatic Programmer states "Refactor early, refactor often".

Other Answer2

  1. Whenever it smells, I refactor. I may not make it perfect now, but I can at least make a small step towards a better state. And those small changes do add up over time...
  2. If I am in the middle of something when I notice the smell, and fixing it isn't trivial (or I am just before release), I may make a (mental or paper) note to return to it once I am finished with the primary task.
  3. Practice makes one better :-) But if I don't see a solution to a problem, I put it aside and let it brew for a while, discuss it with coworkers, or even post it on SO ;-)
  4. If I don't have unit tests and the fix isn't trivial, I start with the tests. If the tests aren't trivial either, I apply point 2.

comments:

+1: Quote Grandma Beck - "If it smells, change it."
@donroby, wasn't it Martin Fowler actually? Of course, both could have invented the saying, but at least I think I have read it first in Refactoring.
+1 for the smell test.
It was in Refactoring, but in a chapter on code smells with Kent Beck as collaborator. And the quote was attributed to Grandma Beck.
@donroby, thanks, I didn't have my copy with me to check :-)

Other Answer3

I start to refactor as soon as I find I am repeating my self. DRY principles ftw.

Also, if methods/functions get too long, to the point where they look unwieldy, or their purpose is being obscured by the length of the function, I break it into private subfunctions that explain what is really going on.

Lastly, if everything's up and running, and the code is dog-slow, I start to look at refactoring for the sake of performance.

comments:

+1, but I'd suggest separating the concepts of refactoring and optimization.
Possibly, but the cases where I've really made headway on some awesome and elegant refactorings tend to to have been spurred on by performance enhancements. Especially in cases of legacy code.

Other Answer4

When implementing a new feature I often notice that the task would be much simpler if the code I'm working on was structured in a different way. In this case I usually step back, try to do the refactoring first, and only after this is done I continue implementing the new feature.

I also have a habit to track all potential improvements that come to my mind either in notes or the bug tracker. The ideas bake there for some time, some of them don't feel so compelling anymore, and the reasonable ones are implemented during a day which I dedicate to smaller tasks.

Other Answer5

Refactor code when it needs to be refactored. Some symptoms I look for:

  • duplicate code in similar objects.
  • duplicate code in within methods of one object.
  • anytime the requirements have changed twice or more.
  • anytime somebody says "we will clean that up later".
  • any time I read through code and shake my head thinking "what goofball did this" (even when the goofball in question is me)

In general, less design and/or less clear requirements means more oppurtunities for refactoring.

Other Answer6

This might sound like a joke, but really, I only refactor when things "get messy". When a simple task starts taking more time and usual, when I have to twist my mind around to remember what function is doing what and such. Also, if the code starts running slow and it's not because I'm running in a development enviroment (a lot of variable outputs and such) if I can't optimise it, I refactor the code. As you said, it's worthed on the long run.

Still, I allways make sure I have enough time to think things through before I start so I don't get in this sittuation.

Cheers!

comments:

I disagree. This attitude is what leads you to situations where you never have time to refactor. Put it off until it really hurts - and then, it hurts too much. Instead, refactor constantly, in little steps, always leaving your code better than when it started - then instead of "getting messy", your code will "get clean".
@Carl Manaster It really depends on what you understand by "getting messy". What I was saying is more or less within the lines of the first answer here, just never knew there's actually a name for that. I never put it off until I don't have time to refactor. Regarding the small steps, I think they're necessary, small things like changing variable types or approaches in a method aren't really refactoring in my opinion. Refactoring comes in place when you rethink classes and approaches on a bigger scale, but that might be just me.
I recommend you read Martin Fowler's book, where he lays out a careful and clear definition of the term "refactoring". It's also where you can find the "change it when it smells" quote. I'm using his definition, and that very clearly includes refactorings as trivial as Rename Method. You say these things aren't refactoring "in your opinion", and that's fine, but working from a well laid-out, standard reference definition of an important term like his helps us to hold more productive discussions.
@Carl Manaster I see what you mean. Sorry if I'm not up to date with the terms, in practice I take the same steps. Will read Martin Fowler's book, thanks for the suggestion!

Other Answer7

I usually refactor when one of the following is true:

  • I have nothing better to do and waiting for the next project to come to my inbox
  • The additions/changes I'm making to the code cannot work unless, or would be better if, I refactor
  • I am aesthetically displeased with the way the code is laid out

Other Answer8

Martin Fowler in his book if the same name suggests you do it the third time you're in a block of code to make another change. First time in the block, you happen to notice you should refactor, but don't have time. Second time back...same thing. Third time back-now refactor. Also, I read the developers of a current release of smalltalk (squeak.org, I think) say they go through a couple weeks of intense coding...then they step back and look at what can be refactored. Personally I have to resist the impulse to refactor as I code or I get 'paralyzed'.