|
Refactoring is the process of clarifying and simplifying
the design of existing code, without changing its behavior.
Un-refactored code tends to rot. Rot takes several forms:
unhealthy dependencies between classes or packages, bad allocation
of class responsibilities, duplicate code, and many other
varieties of confusion and clutter. (Check out this
list of such "design smells.")
Rot is what makes code difficult to maintain or extend. Every
time we change code without refactoring it, rot worsens and spreads.
Code rot frustrates us, costs us time, and unduly shortens the
lifespan of useful systems.
Refactoring code ruthlessly prevents rot, by keeping code
easy to maintain and extend. This extensibility is the reason
to refactor and the measure of its success. This is what enables
XP teams to embrace arbitrary and drastic change. Note that the
XP practice of Test-driven Development (TDD) is essential to
refactoring. The exhaustive tests produced by TDD are what
make it safe and orderly to make changes of any kind. This is
why Adaption always teaches TDD and refactoring together.
What does "refactor ruthlessly" mean? It means striving as a matter
of routine to keep the code's design simple and crystal clear. It means
knowing the design principles and patterns that are vital to keeping code
extensible, and knowing when to "refactor toward" them. It means
refactoring both production code and test code frequently during the
day, eliminating all forms of the "code smells" that are precursors to
true rot.
Mainly it means never going home at the end of the day with "code debts"
that need paying tomorrow (smelly sections that need cleaning up). This
level of code hygiene may at first seem like a lot of extra work, but it
pays you such dividends so soon and so regularly that you soon become
ddicted to it -- rather like TDD.
Refactorings are the opposite of fiddling endlessly with code; they are
precise and finite.
Martin Fowler's definitive
book on the subject
describes 72 specific "refactorings" by name (e.g., "Extract Method,"
which extracts a block of code from one method, and creates a new method
for it). Each refactoring converts a section of code (a block, a method, a
class) from one of 22 well-understood "smelly" states to a more optimal
state. It takes awhile to learn to recognize refactoring opportunities,
and to implement refactorings properly. This is a practice at the heart
of high software craft.
In a TDD context, refactoring has the same flow as any other code change.
You have your automated tests. You begin the refactoring by making the
smallest discrete change you can that will compile, run, and function.
Wherever possible, you make such changes by adding to the existing code,
in parallel with it. You run the tests. You then make the next small
discrete change, and run the tests again. When the refactoring is in
place and the tests all run clean, you go back and remove the old smelly
parallel code. Once the tests run clean after that, you are done.
Badabing, badaboom: cleaner code, with no new bugs.
Fortunately, more and more Integrated Development Environments (IDEs)
are building in automated refactoring support. For example, Adaption's
favorite IDE for Java is eclipse,
which includes more auto-refactorings all the time. Another favorite
is IntelliJ IDEA.
To refactor code in eclipse or IDEA, you select the code you want to
refactor, pull down the specific refactoring you need from a menu, and
the IDE does the rest of the hard work. You are prompted appropriately
for new names for things that need naming, and for similar input. You
can then immediately rerun your tests to make sure that the change
didn't break anything. If anything was broken, you can easily undo
the refactoring and investigate.
Be sure to browse the excellent resources that Martin Fowler maintains
at refactoring.com.
[Next Practice]
|