Slide 1

Slide 1 text

Working Effectively with Legacy Code

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Legacy code is...

Slide 4

Slide 4 text

code that is hard to change

Slide 5

Slide 5 text

a mess

Slide 6

Slide 6 text

legacy code doesn’t need to be old

Slide 7

Slide 7 text

code without tests

Slide 8

Slide 8 text

Repeat with me... ● You can write very good code in a sea of legacy code

Slide 9

Slide 9 text

Repeat with me... ● You can write very good code in a sea of legacy code ● You can uglify code to be able to improve/test it

Slide 10

Slide 10 text

Repeat with me... ● You can write very good code in a sea of legacy code ● You can uglify code to be able to improve/test it ● This process is slow

Slide 11

Slide 11 text

Repeat with me... ● You can write very good code in a sea of legacy code ● You can uglify code to be able to improve/test it ● This process is slow ● Most of the time we are modifying code (and not adding)

Slide 12

Slide 12 text

1. Edit and Pray 2. Cover and Modify

Slide 13

Slide 13 text

Refactoring

Slide 14

Slide 14 text

Unit Tests

Slide 15

Slide 15 text

Strategies 1. Identify change points 2. Find test points 3. Break dependencies 4. Write tests 5. Make changes and refactor

Slide 16

Slide 16 text

Strategies 1. Identify change points 2. Find test points 3. Break dependencies 4. Write tests 5. Make changes and refactor

Slide 17

Slide 17 text

Seams

Slide 18

Slide 18 text

a place where you can alter behavior without modifying the code in that place

Slide 19

Slide 19 text

Seams ● Preprocessing seams: with macros or plugins

Slide 20

Slide 20 text

Seams ● Preprocessing seams: with macros or plugins ● Link seams: with different libraries

Slide 21

Slide 21 text

Seams ● Preprocessing seams: with macros or plugins ● Link seams: with different libraries ● Object seams

Slide 22

Slide 22 text

Seams ● Preprocessing seams: with macros or plugins ● Link seams: with different libraries ● Object seams ● Every seam has an enabling point, a place where you can make the decision to use one behavior or another.

Slide 23

Slide 23 text

Is this a seam? cell.recalculate();

Slide 24

Slide 24 text

Seams cell.recalculate();

Slide 25

Slide 25 text

This is not a seam public void buildSheet() { Cell cell = new FormulaCell(this, "", ""); cell.recalculate(); }

Slide 26

Slide 26 text

Seams public void buildSheet(Cell cell) { cell.recalculate(); }

Slide 27

Slide 27 text

Is this a seam? public void buildSheet(Cell cell) { recalculate(cell); } private static void recalculate(Cell cell) { }

Slide 28

Slide 28 text

Break dependencies 1. Sensing: break dependencies to sense when we can’t access values our code computes.

Slide 29

Slide 29 text

Break dependencies 1. Sensing: break dependencies to sense when we can’t access values our code computes. 2. Separation: break dependencies to separate when we can’t even get a piece of code into a test harness to run.

Slide 30

Slide 30 text

What if I don’t want to write tests?

Slide 31

Slide 31 text

What I don’t want to write tests... ● Sprout method

Slide 32

Slide 32 text

Sprout method public void postEntries(List entries) { for (Entry entry : entries) { entry.postDate(); } transaction.getListManager().addAll(entries); }

Slide 33

Slide 33 text

Sprout method public void postEntries(List entries) { List entriesToAdd = new LinkedList(); for (Entry entry : entries) { if(!transaction.getListManager().contains(entry)) { entry.postDate(); entriesToAdd.add(entry); } } transaction.getListManager().addAll(entriesToAdd); }

Slide 34

Slide 34 text

Sprout method public void postEntries(List entries) { for (Entry entry : entries) { entry.postDate(); } transaction.getListManager().addAll(entries); }

Slide 35

Slide 35 text

Sprout method public void postEntries(List entries) { List filteredEntries = uniqueEntries(entries); for (Entry entry : filteredEntries) { entry.postDate(); } transaction.getListManager().addAll(filteredEntries); }

Slide 36

Slide 36 text

What I don’t want to write tests... ● Sprout method ● Sprout class

Slide 37

Slide 37 text

What I don’t want to write tests... ● Sprout method ● Sprout class ● Wrap method

Slide 38

Slide 38 text

Wrap method public void pay() { Money amount = new Money(); for (Timecard card : timecards) { if (payPeriod.contains(date)) { amount.add(card.getHours() * payRate); } } payDispatcher.pay(this, date, amount); }

Slide 39

Slide 39 text

Wrap method public void dispathPayment() { Money amount = new Money(); for (Timecard card : timecards) { if (payPeriod.contains(date)) { amount.add(card.getHours() * payRate); } } payDispatcher.pay(this, date, amount); }

Slide 40

Slide 40 text

Wrap method public void pay() { logPayment(); dispatchPayment(); }

Slide 41

Slide 41 text

What I don’t want to write tests... ● Sprout method ● Sprout class ● Wrap method ● Wrap class

Slide 42

Slide 42 text

What I don’t want to write tests... ● Sprout method ● Sprout class ● Wrap method ● Wrap class ● Follow dependency tips

Slide 43

Slide 43 text

FAQ

Slide 44

Slide 44 text

I don’t understand the code to change it ● Read it many times (d’oh!) ● Do sketches ● Extract small methods ● Scratch Refactoring

Slide 45

Slide 45 text

Class too big and I don’t want it to get any bigger ● Divide responsibilities (techniques!)

Slide 46

Slide 46 text

Class too big and I don’t want it to get any bigger ● Divide responsibilities (techniques!) ● Look for group methods

Slide 47

Slide 47 text

Class too big and I don’t want it to get any bigger ● Divide responsibilities (techniques!) ● Look for group methods ● Look at hidden methods

Slide 48

Slide 48 text

Class too big and I don’t want it to get any bigger ● Divide responsibilities (techniques!) ● Look for group methods ● Look at hidden methods ● Look for decisions that can change together

Slide 49

Slide 49 text

Class too big and I don’t want it to get any bigger ● Divide responsibilities (techniques!) ● Look for group methods ● Look at hidden methods ● Look for decisions that can change together ● Look for internal relationships

Slide 50

Slide 50 text

Class too big and I don’t want it to get any bigger ● Divide responsibilities (techniques!) ● Look for group methods ● Look at hidden methods ● Look for decisions that can change together ● Look for internal relationships ● Look for the primary responsibility of the class

Slide 51

Slide 51 text

Class too big and I don’t want it to get any bigger ● Divide responsibilities (techniques!) ● Look for group methods ● Look at hidden methods ● Look for decisions that can change together ● Look for internal relationships ● Look for the primary responsibility of the class ● Do some scratch refactoring

Slide 52

Slide 52 text

Class too big and I don’t want it to get any bigger ● Divide responsibilities (techniques!) ● Look for group methods ● Look at hidden methods ● Look for decisions that can change together ● Look for internal relationships ● Look for the primary responsibility of the class ● Do some scratch refactoring ● Focus on the current work

Slide 53

Slide 53 text

My application is all API calls ● Skin and wrap the API ● Responsibility-Based Extraction

Slide 54

Slide 54 text

My application has no structure ● Tell the story of the system.

Slide 55

Slide 55 text

What methods should I test? ● Do characterization tests

Slide 56

Slide 56 text

What methods should I test? ● Do characterization tests ● Sketches

Slide 57

Slide 57 text

What methods should I test? ● Do characterization tests ● Sketches ● Look for interception and pinch points

Slide 58

Slide 58 text

Tricks

Slide 59

Slide 59 text

Tricks ● Use DI, fakes, nulls, null objects, extract interfaces... to separate dependencies ● Preserve signatures

Slide 60

Slide 60 text

Tricks ● Minimize coupling points when extracting: the number of values that pass into and out of the method ● Do extractions with these goals: 1. To separate logic from dependencies 2. To introduce seams

Slide 61

Slide 61 text

Tricks ● Do 1 thing at a time

Slide 62

Slide 62 text

Mars Rover

Slide 63

Slide 63 text

Techniques

Slide 64

Slide 64 text

Techniques ● Adapt Parameter: change signature to pass a parameter public void populate(HttpServletRequest request) { String[] values = request.getParameterValues( pageStateName); if (values != null && values.length > 0) { marketBindings.put(pageStateName + getDateStamp(), values[ 0]); } }

Slide 65

Slide 65 text

Techniques ● Adapt Parameter: change signature to pass a parameter public void populate(ParameterSource source) { String[] values = source.getParameterForName(pageStateName); if (values != null && values.length > 0) { marketBindings.put(pageStateName + getDateStamp(), values[ 0]); } }

Slide 66

Slide 66 text

Techniques ● Adapt Parameter: change signature to pass a parameter ● Break Out Method Object: create a class with the method

Slide 67

Slide 67 text

Techniques ● Adapt Parameter: change signature to pass a parameter ● Break Out Method Object: create a class with the method ● Encapsulate Global References

Slide 68

Slide 68 text

Techniques ● Adapt Parameter: change signature to pass a parameter ● Break Out Method Object: create a class with the method ● Encapsulate Global References ● Expose Static Method

Slide 69

Slide 69 text

Techniques ● Adapt Parameter: change signature to pass a parameter ● Break Out Method Object: create a class with the method ● Encapsulate Global References ● Expose Static Method ● Extract and Override Call / Factory Method / Getter

Slide 70

Slide 70 text

Techniques ● Extract and Override Call / Factory Method / Getter protected void rebindStyles() { styles = StyleMaster.formStyles(template, id); }

Slide 71

Slide 71 text

Techniques ● Extract and Override Call / Factory Method / Getter protected void rebindStyles() { styles = formStyles(template, id); } public class TestingPageLayout extends Hello { protected List formStyles(StyleTemplate template, int id) { return new ArrayList(); } }

Slide 72

Slide 72 text

Techniques ● Adapt Parameter: change signature to pass a parameter ● Break Out Method Object: create a class with the method ● Encapsulate Global References ● Expose Static Method ● Extract and Override Call / Factory Method / Getter ● Extract interface

Slide 73

Slide 73 text

Techniques ● Adapt Parameter: change signature to pass a parameter ● Break Out Method Object: create a class with the method ● Encapsulate Global References ● Expose Static Method ● Extract and Override Call / Factory Method / Getter ● Extract interface ● Introduce Instance Delegator

Slide 74

Slide 74 text

Techniques ● Skeletonize / Find Sequences

Slide 75

Slide 75 text

Techniques ● Skeletonize / Find Sequences ● Introduce Static Setter

Slide 76

Slide 76 text

Techniques ● Skeletonize / Find Sequences ● Introduce Static Setter ● Parameterize Constructor / method

Slide 77

Slide 77 text

Techniques ● Skeletonize / Find Sequences ● Introduce Static Setter ● Parameterize Constructor / method ● Replace Global Reference with Getter

Slide 78

Slide 78 text

Techniques ● Skeletonize / Find Sequences ● Introduce Static Setter ● Parameterize Constructor / method ● Replace Global Reference with Getter ● Supersede Instance Variable

Slide 79

Slide 79 text

Techniques ● Skeletonize / Find Sequences ● Introduce Static Setter ● Parameterize Constructor / method ● Replace Global Reference with Getter ● Supersede Instance Variable ● Text redefinition

Slide 80

Slide 80 text

What to draw from this book...

Slide 81

Slide 81 text

What to draw from this book... ● Techniques to allow safe changes to enable unit tests ● The concept of Seams

Slide 82

Slide 82 text

About the book itself (IMHO) ● FAQ & Catalogue ● Java/C/C++ ● Long ● Many refactoring examples ● Very test focused ● I don’t love this book

Slide 83

Slide 83 text

questions? @nhpatt @cylicon_valley @agilespain

Slide 84

Slide 84 text

No content

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

Working Effectively with Legacy Code