working with legacy code

33
Working with legacy code Andrea Polci

Post on 22-Oct-2014

4.519 views

Category:

Technology


1 download

DESCRIPTION

Based on Michael C. Feathers book "Working with Legacy Code"

TRANSCRIPT

Page 1: Working With Legacy Code

Working with legacy code

Andrea Polci

Page 2: Working With Legacy Code

Legacy code: definition

“source code inherited from someone else and source code inherited from an older version of the software”

(wikipedia)

“code without tests”(Michael Feathers)

Page 3: Working With Legacy Code

Legacy code: caracteristics

● Poor architecture● Stratification of modifications● Non-uniform coding styles● Poor written documentation● “Oral” documentation lost● No tests

● Extremely valuable!● Only successful code become legacy code

Page 4: Working With Legacy Code

Why we have legacy code?

● More and more features● Shortcuts and hacks● Developer rotation● Development team growth

● less communication

Page 5: Working With Legacy Code

Why we need to change legacy code?

● New functionality● Bug● Refactoring● Optimization

Page 6: Working With Legacy Code

Options● Start from scratch?● Look for a new Job?● May be we need

Rambo?● Or Mc Gyver?● May be we need

some “tools” to work effectively with legacy code

Page 7: Working With Legacy Code

Test

● What about legacy code?● To modify it we need

tests● To write test we need

to modify the code

● They gives feedback on changes

● Different kind of tests● Unit● Integration● Functional

Page 8: Working With Legacy Code

An algorithm

1) Identify what to change2) Identify what to test3) Break dependencies4) Write the tests5) Modify and refactoring

Page 9: Working With Legacy Code

What to change

1) Identify what to change● Do we have enough knowledge to choose where

to make changes?● Sometimes we need to modify many different

places just to make a simple change.2) Identify what to test3) Break dependencies4) Write the tests5) Modify and refactoring

Page 10: Working With Legacy Code

I don't understand the code

● Note/Sketching● Listing Markup● Scratch Refactoring● Write tests● Delete Unused Code

● There is the repository for that

Page 11: Working With Legacy Code

I don't understand the structure

● Long lived applications tend to loose structure● It takes long time to understand the big picture● There is no big picture● The team is in emergency mode and lose sight of the

big picture● It's important that every developer understand the

big picture or the code will diverge from the architecture● Tell the story of the system● Naked CRC● Conversation Scrutiny

Page 12: Working With Legacy Code

What to test

1) Identify what to change2) Identify what to test

• Can be an hard work• Effect analisys• How much time do we have?

3) Break dependencies4) Write the tests5) Modify and refactoring

Page 13: Working With Legacy Code

I don't have the time to test

● Be careful!● Sometimes there is no other option● Don't make it worse!

● Try to isolate new (tested) code from legacy code● Sprout method/class● Wrap method/class

Page 14: Working With Legacy Code

Sprout method

public class ProductLablePrinter{ … public void printLabel(int productId) { String barcode; … // compute barcode … // print barcode }}

Page 15: Working With Legacy Code

Sprout method

public class ProductLablePrinter{ … public void printLabel(int productId) { String barcode; … // compute barcode …

logPrinted(barcode); // print barcode }

protected void logBarcode(String barcode) { // My code }

}

Page 16: Working With Legacy Code

Break Dependencies

1) Identify what to change2) Identify what to test3) Break dependencies

● How do I put a class in a test harness?● How I know I'm not breaking anything?

4) Write the tests5) Modify and refactoring

Page 17: Working With Legacy Code

Sensing & Separation

● Sensing:we break dependencies to sense when we can't access values our code computes

● Separation:we break dependencies to separate when we can't even get a piece of code into a test harness to run

Page 18: Working With Legacy Code

Sensing & Separation: Example

public class ProductLablePrinter{ … Public void printBarcode(int productId) { String barcode; … // compute barcode … // print barcode; }}

Page 19: Working With Legacy Code

Seam

● Seam:a place where you can alter behavior of your program without editing in that place

● Enabling Point:Every seam has an enabling point, a place where you can make the decision to use one behaviour or another

● Looking for existings seams in legacy code allow us to break dependencies (for sensing or separation) without refactoring.

Page 20: Working With Legacy Code

Different kind of Seams

● Preprocessor seams

● Link seam

● Object seam

Page 21: Working With Legacy Code

Object seam: Examplepublic class ProductLablePrinter{ … public void printLabel(int productId) { String barcode; … // compute barcode … printBarcode(barcode); }

protected void printBarcode(String barcode) { // access to printer }}

Public void testPrintBarcode() { ProductLablePrinter plp = new ProductLabelPrinter(){ String lastPrinted = null; protected void printBarcode(String barcode) {lastPrinted=barcode;} }

// … test code}

Page 22: Working With Legacy Code

I can't get this class into a Test

● Objects of the class can't be created easily● Parameters we have to pass to the constructor● Hidden dependencies

● The test harness won't easily build with the class in it

● The constructor we need to use has bad side effects

● Significant work happens in the constructor and we need to sense it

Page 23: Working With Legacy Code

Example (1)public void testLabelPrinter() { new LabelPrinter();}

The constructor LabelPrinter is undefined

Page 24: Working With Legacy Code

Example (2)public class LabelPrinter { public LabelPrinter(Printer prn, Warehose wh) { … this.printer = prn; if(!this.printer.isOnline()) { throw new … } }}

public void testLabelPrinter() { Printer prn = new Printer(“stampante”); Warehouse wh = new Warehouse(“magazzino1”); LabelPrinter labPrint = new LabelPrinter(prn, wh);}

public class Printer { boolean isOnline(){ … } void startJob() { … } void printLine(String line) { … } void endJob() { … }}

Page 25: Working With Legacy Code

Example (3)

public interface PrinterInterface { boolean isOnline(); void startJob(); void printLine(String line); void endJob();}public class Printer implements PrinterInterface { …}public class LabelPrinter { public LabelPrinter(PrinterInterface prn, Warehose wh) { ... }}

Page 26: Working With Legacy Code

I can't run this method in a test

● Method not accessible to the test● It's hard to construct the parameters● Side effects (database, access to

hardware, ecc.)● Need to sense through objects used by the

method

Page 27: Working With Legacy Code

Test

1) Identify what to change2) Identify what to test3) Break dependencies4) Write the tests5) Modify and refactoring

Page 28: Working With Legacy Code

Modify and refactoring

1) Identify what to change2) Identify what to test3) Break dependencies4) Write the tests5) Modify and refactoring

● TDD● Programming by difference

Page 29: Working With Legacy Code

Making changes takes too much time

● Understanding● How can we increase our understanding of the code?

● Lag Time● It takes to much time to see the effect of changes and

this slow down the development● Built time● Slow tests● No unit tests● Often to solve this we need to break dependecies

Page 30: Working With Legacy Code

Tools

● Authomatic refactoring tools● Can we trust them?

● Mock Objects● Unit test harnesses

● JUnit● Other test harnesses

● Non-unit tests

Page 31: Working With Legacy Code

Conclusions

● No “silver bullet” here● It's an hard work but (usually) not

impossible● At first it will seems overwhelming, but

things will get better as the number of tests increase

● Be pragmatic!

Page 32: Working With Legacy Code

Questions?

Page 33: Working With Legacy Code

References

● Workking Effectively with Legacy Code,Michael C. Feathers

● Joel on Software: Things you should never do, part I

http://www.joelonsoftware.com/articles/fog0000000069.html

● Michael Dubakov: Refactoring vs Rewritehttp://www.targetprocess.com/blog/2009/11/refactoring-vs-rewrite.html