Top Banner
Working with legacy code Andrea Polci
33

Working With Legacy Code

Oct 22, 2014

Download

Technology

Based on Michael C. Feathers book "Working with Legacy Code"
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
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