søndag den 21. juni 2009

Working Effectively with Legacy Code (WELC)

Well, two weeks went by pretty fast, and I haven't had very much time to myself, so I have still not finished my new Bible, WELC ... however, I've been preaching the subject to several colleagues and members of the development community, and I've received lots of positive feedback. This affirms my belief, that this book will change the way I (and hopefully other developers around me too) works with legacy systems.

In this post I'll dive into one of the concrete techniques from the book, so You get an idea of why I'm so excited about the book. The technique I have chosen is from chapter 6 "I Don't Have Much Time and I Have to Change It" from the book, and it is merely meant as a teaser - I want You to read the book for yourselves and form Your own opinions. I will show more methods soon.

The "Wrap Method" technique.

The main problem you face, when having to add new functionality to a legacy system, wether a bug fix or a new feature, is inadvertently introducing new bugs. Since legacy systems does not have (sufficient) tests, changing code is feeling close to Russian Roulette, only with 5 bullets and 1 empty slot in the revolver, and not the other way around.

The Wrap Method technique allows you to add functionality to existing methods without the risk of introducing new bugs. Sounds like magic, right? Well it isn't, it is just a very straightforward application of encapsulation and separation of concerns.

The idea is, that if you can characterize your change as appending new functionality at the beginning or at the end of an existing method, you create a new method containing the new functionality plus a call to the existing method. Then you swap the names of the existing method and the new method. This means, that every call to the original method suddenly hits the new method, which in turn calls the original method before or after executing the new functionality.

Here is an example. The following method is the one you want to add functionality to:

public void MethodA([lots of parameters])
{
// Hundreds or thousands of lines of complex functionality
}

You want to include a call to another method, MethodB, in MethodA. However MethodA is complex enough as it is. So we add another method, NewMethod:

public void NewMethod([same parameters as MethodA])
{
MethodB([some parameters]);
MethodA([lots of parameters]);
}

Finally we swap the names of MethodA and NewMethod:

public void NewMethod([lots of parameters])
{
// Hundreds or thousands of lines of complex functionality
}

public void MethodA([same parameters as MethodA])
{
MethodB([some parameters]);
NewMethod([lots of parameters]);
}

This way, all calls to the original MethodA now hits the new MethodA, which includes a call to MethodB, and we have not changed a single line of code inside MethodA. So the only potential bugs introduced is in the call to MethodB - quite a bit easier to debug than debugging MethodA, right?

This technique demonstrates one of Michael Feathers' principles: Don't change code without tests to validate your change.

Ingen kommentarer:

Send en kommentar