|
In
this third article of the series on Extreme Programming (XP), PRADYUMN
SHARMA looks at another important XP practice called refactoring,
which is an ongoing process to improve the quality of existing software
Suppose you had designed some software two years
ago, and had subsequently implemented it, tested it thoroughly and
successfully deployed it for some client. You also know that you
had done a good job of its design and implementation, applying sound
practices of software design, appropriate design patterns, etc.
As commonly happens, some time after commencing
its usage, the customer came up with some minor changes in requirements,
which you could easily incorporate in the system because of a good
design and good documentation (I seem to be kidding here, but let
us anyway assume so). Sometime later, some more changes in customer’s
requirements, and therefore, some more modifications to your code.
A new report here and a different validation there, and some more
code got modified further. Such modifications, mostly minor, have
been going on for quite some time now.
Now today, you get yet another minor enhancement
request from the client. As you start looking at the related code,
you suddenly realise that over a period of time, the small changes
made by you to some class, or a group of related classes in a package,
has resulted in that code becoming overly co-mplex or inelegant.
Or, one class ending up doing too much work that does not logically
belong to this class.
Software decay
As you look at some more classes, you find that
it is not an isolated case where a class that was compact, efficient
and elegant two years ago, is no longer so. In short, you are encountering
a kind of software decay.
Like everything else in the universe, software
also decays. Though unlike tangible things, software does not decay
by just using it, but due to changes made to it in order to meet
some short-term goals.
How does one normally respond to software decay?
Most of the times, by just cursing one’s predecessors, and
proceeding to make more changes to the concerned code in order to
meet the new requirements. And in the process, contributing further
to the software decay. Over a period of time, the software becomes
more and more messy.
As the original developers get out of the picture,
and new people get involved in the project, they find it increasingly
difficult to maintain the software. But nobody has the courage to
overhaul the system and clean it up. After all, nobody wants to
risk breaking something else and take the flak. By now, perhaps
there is nobody who fully comprehends the structure of the entire
system.
But how should one tackle software decay? By
adopting the XP practice of refactoring in your project.
Introducing refactoring
Refactoring is the process of making changes
to the design of an existing system in order to improve its quality.
You take a bad piece of code, and make some changes to it with the
intention of making it simpler to read and maintain, or more efficient,
or more in accordance with the good design practices. When you are
doing refactoring, you are not adding any new functionality to the
system, but making it easier to do so in future.
Martin Fowler has described the principles and
techniques of refactoring in his celebrated book titled Refactoring.
This book discusses the guidelines about how to identify where refactoring
is requ-ired, and how to en-sure that it is done in a safe manner.
For anyone wanting to master this craft, this book is an essential
reading.
Refactoring has to be done in a controlled, disciplined
manner without chan-ging the external behaviour of the code. The
structure of the system should be improved, but without introducing
new bugs in the system.
Examples
As one simple example of refactoring, you may
find that a method in a class has become too large, obfuscating
its purpose. You feel it would be better to split that method into
smaller ones, each having a distinct sub-task to handle. Go ahead
and do that. This is anyway simple, because no client of this code
gets affected by it.
Somewhere else you may find that a lot of subclasses
are duplicating similar code. It may be better to move this common
code into a superclass. You can do this also without affecting any
client code.
As a more complex example, somewhere you find
that a class is doing some work that should be done by some other
class. This involves not only moving the method from one class to
another, but also modifying the code of the clients of this class,
so that they make a call on a different class.
In some other place, a method in a class may
need more information from its caller. You can simplify this interaction
by passing the required information as a parameter in the method.
This change also req-uires modifying the interface of the class
as well as code in its clients.
Refactoring and testing
At first, such changes may sound scary. You are
now going to make changes in multiple places within your application.
But how do you ensure that while it improves the structure of your
system, it does not break something else that has been You can do
this refactoring with confidence if you have already understood
and implemented the XP practice of testing (discussed in the previous
article of this series). In fact, automated tests are a must for
doing these refactorings with confidence. Any class that is going
to be modified must have such self-checking tests.
If you do not already have automated tests for
the code being modified, you now build such a test suite. Go through
the code that you intend to refactor, and create tests for them
first.
Then, refactor your code. But don’t release
the modified code unless it passes all the tests again. If any tests
fail, it means that your refactoring has broken something that was
working correctly earlier (of course, it may also mean that your
tests are wrong, so it is important that the tests themselves are
written carefully and verified before they are run).
Continuous refactoring
Begin refactoring in a small localised manner.
Keep refactoring wherever you find a scope for improvement. You
may have to alternately switch between a development role adding
new functionality, and a refactoring role cleaning up earlier code.
Once you have a clear picture of the problems
with the overall structure of a system at a large level, you may
then even consider big refactorings, involving altering the system
architecture, changing the key inheritance hierarchies in the system,
etc.
Refactoring should be an on-going activity, and
not just left for maintenance stage problems. In fact, once you
master the practices of refactoring and experience its benefits,
you will find that it is no longer critical to have a watertight
design before commencing any coding. Indeed the design keeps evolving
as the development proceeds.
Pradyumn Sharma is the CEO of Pragati Software,
Mumbai. He is a trainer and consultant in the area of object technology.
E-mail: pradyumn.sharma@pragatisoftware.com
|