Reputation: 43214
The TDD circle is:
"Write failing Test" -> "Write Code to fit a Test" -> "Refactor"
At "Coding" step we are assumed to write code as simple as possible, just to fix a failing test. We should not write complex code until it is really required.
The next step is Refactor. Should we refactor just written code? I think there is no real sence, as we should be happy with code as far as tests are passing.
Probably Refactoring activity should be forced by some thing, like Code writing is foced by failing Tests. Here some possible factors:
What other reasons you see to start the Refactoring?
Also, is this scheme correct:
"Write failing Test" -> "Code" -> "Refactor" -> "Write failing Test"
or may be it should be seen as
"Write failing Test" -> "Code/Refactor" -> "Write failing Test"
+
"External factor (like bad performance)" -> "Refactor".
Upvotes: 5
Views: 5188
Reputation: 584
Rule of Three:
When you are doing something for the first time, just get it done.
When you are doing something similar for the second time, cringe at having to repeat but do the same thing anyway.
When you are doing something for the third time, start refactoring.
When adding a feature:
Refactoring helps you understand other people's code. If you have to deal with someone else's dirty code, try to refactor it first. Clean code is much easier to grasp. You will improve it not only for yourself but also for those who use it after you.
Refactoring makes it easier to add new features. It's much easier to make changes in clean code.
When fixing a bug:
Bugs in code behave just like those in real life: they live in the darkest, dirtiest places in the code. Clean your code and the errors will practically discover themselves.
Managers appreciate proactive refactoring as it eliminates the need for special refactoring tasks later. Happy bosses make happy programmers!
During a code review:
The code review may be the last chance to tidy up the code before it becomes available to the public.
It's best to perform such reviews in a pair with an author. This way you could fix simple problems quickly and gauge the time for fixing the more difficult ones.
Upvotes: 0
Reputation: 21996
TDD is a great tool to keep you on track/on task. The problem with:
"Write failing Test" -> "Code/Refactor" -> "Write failing Test"
you propose, is it can easily become:
"Write failing Test" -> "Refactor" -> "Code" -> "Write failing Test"
or then
"Write failing Test" -> "Refactor" -> "Refactor" -> "Refactor" -> "Code" -> "Write failing Test"
which is what you want to avoid. By refactoring at the beginning of implementation, you are indulging in speculative development, and, not achieving the goal of the coding session. It's easy to head off on tangents and build things you don't necessarily need. If you have the feature working and tests passing, it's much easier to decide when's the right time to stop refactoring. And you can stop at any time because your tests are passing.
Additionally, you don't want to refactor when your tests aren't green.
A couple other small pts:
I think most of the literature has a slightly different definition what refactoring is. It's not "some changes to the system" or performance enhancements, but specific changes that don't change the behavior but improve the design. If you accept the definition, then performance improvements don't really qualify: they are normal development tasks that need their own acceptance tests. I usually try to frame these as end-user facing stories, where the benefit of doing them is clear. Make sense?
I think you're right that the TDD practice doesn't specifically address the design problems revealed during code reviews. (See reflections and pair programming for other solutions to this.) These tend to be bigger, cross-story issues built up as "code debt", and have to take some time to clean it up periodically. This could be a separate project, but I, personally, always like to do this as part of another "real" story. Last time I did this, we identified we had a problem, but ended up waiting a few weeks until we had a related story to work on it. We followed the TDD practice of implementing the new feature first-- even though we knew it was way wrong. But then we really understood what was going on and why it was messy, and then spent longer than usual on the refactor phase. Worked well.
Upvotes: 6
Reputation: 15927
Is there a bad time to refactor other than a week or two before a major release?
Upvotes: 1
Reputation: 532435
Other than refactorings triggered by the need to improve performance, I usually will notice while I am writing the code to pass a test a place where I could share code or a better, more elegant way to accomplish something. At this point, you finish the code to pass the test, make sure all tests are passing, then go back and do your refactorings. This may or may not involve the exact code that you wrote, most likely it will, but it may be something that you just noticed while writing the new code.
Sometimes I'll note something that could be refactored, but decide that what I'm working on right now is more important. At that point I'll note the issue for a later refactoring. Eventually, that refactoring will become as or more important than the next feature and it will get done in the refactor phase of a red/green/refactor cycle. In this case, it probably is unrelated to what I was just working on.
Upvotes: 1
Reputation: 11277
You can write some pretty ugly code while getting a test to pass; refactor now not because it doesn't work, but it's not very maintainable. That's the point case.
After writing code to fit several tests, you can start taking a bigger picture look -- are there overlaps between those pieces of code, where you can factor out some duplication?
Upvotes: 9
Reputation: 3889
Hmm... I usually consider these sort of "external" refactorings separate from the TDD cycle itself. After finishing up feature XYZ using TDD, one ought to have a healthy set of tests to prevent from introducing bugs via refactoring (assuming code coverage is optimal, etc.). Performance bottlenecks and hard-to-understand code usually crop up after the fact anyway, so doing refactoring at that point would be ideal, I think. You can improve performance, make the code easier to understand, and do whatever else that needs doing while using the tests to make sure that you're not introducing bugs into the system.
So to answer your question, I do not believe that external refactoring fits into the TDD pattern, although the identifiers (code smells if you will) are definitely items to keep track of as you are developing the code.
Upvotes: 1