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".
The best time to consider refactoring is before adding any updates or new features to existing code. Going back and cleaning up the current code before adding in new programming will not only improve the quality of the product itself, it will make it easier for future developers to build on the original code.
Refactoring is a mandatory skill for Agile Teams and is a critical component of the Team and Technical Agility competency of the Lean Enterprise. Refactors should routinely appear on the Team Backlog and be included—along with in-line refactoring—in story estimates.
In the beginning, the easiest way to start with refactoring is to do it on a method level. A quick thing to check first is if the method is too long. An extended method is a classic sign of the process being too much, and it would probably benefit by splitting it into several other smaller methods.
8.2. The first step in the refactoring process is to analyze the code base and identify refactoring candidates. Projects can carry out manual code/design reviews and employ automated code/design analysis tools to find smells and determine candidates for refactoring (See Section 8.1 on refactoring tools).
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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With