Refactoring Legacy Code - true story

98
Refactoring legacy code True story

description

 

Transcript of Refactoring Legacy Code - true story

Page 1: Refactoring Legacy Code - true story

Refactoring legacy code

True story

Page 3: Refactoring Legacy Code - true story

Aki Salmi@rinkkasatiainen

• Hiking guide• Supervisor • Programmer

• Blipper – but no pics today.

Page 4: Refactoring Legacy Code - true story

Refactoring legacy code

True story

Page 5: Refactoring Legacy Code - true story

My goals for the talk

• Show how what I have learned at coderetreats have been taken into production use• GOAL 1: Encourage us to practice the craft

• Show what I’ve done – Test Driving my design• Show steps rather than finished ‘product’• GOAL 2: initiate discussions on what I’ve done

• Learn• GOAL 3: reflect on my work and decisions and how

they actually seem now.

Page 6: Refactoring Legacy Code - true story

Tips for listeners

• There’s going to be a lot of code

• Rather than reading the code, try to smell it.

• How clean the code seems to be?

Page 7: Refactoring Legacy Code - true story

What I’ve done – what I believe in

• Is not either good or bad. • It has bugs (I’ve seen those)• It provides value to the customer every day

Page 8: Refactoring Legacy Code - true story

What is valuable (for someone)

• Client• clients of the Client• Myself & fellow ambientians

• How likely this code is going to change?• How likely the change I make is going to introduce

bugs in future additions to this feature

Page 9: Refactoring Legacy Code - true story

Key process decisions

• TDD as design tool.• Unit tests for changes• Practice TDD in real environment. Try to get feedback

• Refactor often• Keep tests clean

• ”if it is not important for the test, it is important not to be in the test!”

• Note: Builder pattern

Page 10: Refactoring Legacy Code - true story

Key process decisions

• Use proper tools• GIT, IntelliJ IDEA, VIM

• Hamcrest, mockito, various other open-source components

Page 11: Refactoring Legacy Code - true story

4 elements of simple design

1. Passes its tests

2. Minimizes duplication

3. Maximizes clarity

4. Has fewer elements

Page 12: Refactoring Legacy Code - true story

Coderetreat

• 1 day of coding• Pair programming, 6 different pairing partner• Learn through pairing• Deliberate practice• experiment

Page 13: Refactoring Legacy Code - true story

Key takeaways from coderetreats

• Baby steps – commit to git often. Rebase to keep git log clean

• TDD-as-if-you-meant-it• Avoid conditionals, switches, try/catch• Only 4 lines per method• Avoid naked primitives• Only one assert/behavior per test• Sapir-Whorf hypothesis• Tell – Don’t ask: no getters/setters for objects

Page 14: Refactoring Legacy Code - true story

Object calisthenics

1. One level of indentation per method

2. Don’t use the ELSE keyword

3. Wrap all primitives and Strings

4. First class collections

5. One dot per line

6. Don’t abbreviate

7. Keep all entities small

8. No classes with more than two instance variables

9. No getters / setters / properties

Page 15: Refactoring Legacy Code - true story

Background of the system

Page 16: Refactoring Legacy Code - true story

Domain logicPOJO, Spring, Hibernate

UI (Grails) Controllers + Views

Database

Page 17: Refactoring Legacy Code - true story

A live service since 2008

Providing value to Client and end-users every day since

Page 18: Refactoring Legacy Code - true story

Case 1: adding a search parameter

Page 19: Refactoring Legacy Code - true story

Search for a product

The method had 21 parameters and 200LOC

Page 20: Refactoring Legacy Code - true story

And there’s more

Page 21: Refactoring Legacy Code - true story

..and more

..you see the pattern.

Page 22: Refactoring Legacy Code - true story

What to doadd yet another parameter?

Page 23: Refactoring Legacy Code - true story

What I did was a brief study

• How the methods are used in the service?• Grails-based service (200LOC) uses it

• Determine the current responsibilities• Service builds valid parameters, DAO consumes it.

• Where the changes could be made?• Both the service & DAO

• Is the method likely to change later?• YES

Page 24: Refactoring Legacy Code - true story

Step 1

Page 25: Refactoring Legacy Code - true story

An integration test, See the builder-pattern

If it’s not important for the test, it is important not to be on the test

Page 26: Refactoring Legacy Code - true story

I changed the signature

Page 27: Refactoring Legacy Code - true story

The first model

Page 28: Refactoring Legacy Code - true story

And part of the service

Page 29: Refactoring Legacy Code - true story

Key decisions

• Factory to hide implementation details• Sometimes Criteria handled Date, sometimes Calendar

• Make it first to work, refactor then.

• Create a ProductSearchCriteria per type – think about the name of the object

• There were no tests before – try to make minimal impact on code.

Page 30: Refactoring Legacy Code - true story

Test on localhost.

• The context is a bit more complex than I originally thought:• In one case, it was not enough to limit on Date/Calendar

• Thus, the original factory-idea would turn into a bit more complicated problem

Page 31: Refactoring Legacy Code - true story

Step 2

Page 32: Refactoring Legacy Code - true story

Firstly: factory into builder

• Build what you need – add items to CompositeSearchCriteria as need arises

• Again – do the minimal changes to the grails-service in order to minimize errors

Page 33: Refactoring Legacy Code - true story

Second try

Page 34: Refactoring Legacy Code - true story

Second try

Page 35: Refactoring Legacy Code - true story

Tests for builder

Page 36: Refactoring Legacy Code - true story

Buildermaybe too many responsibilities?

Page 37: Refactoring Legacy Code - true story

Key decisions

• Do the smallest amount that is needed• Builder to support only those methods that are used.

Page 38: Refactoring Legacy Code - true story

Step 3

Page 39: Refactoring Legacy Code - true story

Minor changes to fix a bug

Page 40: Refactoring Legacy Code - true story

Bug # 2

Page 41: Refactoring Legacy Code - true story

Minor changes to fix a bug

Page 42: Refactoring Legacy Code - true story

Bug # 3

Page 43: Refactoring Legacy Code - true story

Sometimes maths just is too much

Page 44: Refactoring Legacy Code - true story

Bug # 4

Page 45: Refactoring Legacy Code - true story

Minor changes to fix a bug

Page 46: Refactoring Legacy Code - true story

Learnings

• Fast to refactor• Next time: start from integration test

• But one cannot integration test the whole – too many parameters

• Minor changes somewhere in the controller code caused it to fail on other places.

• Later, a colleague joined me. His first task was to add a new concept to domain.• Also for search

Page 47: Refactoring Legacy Code - true story

Case 2: Attachment handling

Page 48: Refactoring Legacy Code - true story

Targets

• System used to have ~ 20 different attachment types.

• Only 5 were needed.• Earlier supported only 1 attachment / type /

product. Now should support more.

Page 49: Refactoring Legacy Code - true story

AttachmentService(Dump?)

Page 50: Refactoring Legacy Code - true story

Decision to be made

• Where to start?• The only (business) knowledge came from the

previous changes

• I decided to tackle the switch-case structure• It was spread 4 times throughout the code• To ease the change from 20 to 5

Page 51: Refactoring Legacy Code - true story

A story of FileType

Page 52: Refactoring Legacy Code - true story

AttachmentFileType–RepositoryTest

Page 53: Refactoring Legacy Code - true story
Page 54: Refactoring Legacy Code - true story

AttachmentFileTypeRepos…

Page 55: Refactoring Legacy Code - true story

FileType

Page 56: Refactoring Legacy Code - true story

Step 2

Get rid of switch-case

Page 57: Refactoring Legacy Code - true story

From …

Page 58: Refactoring Legacy Code - true story

… to

Page 59: Refactoring Legacy Code - true story

Or in picture

Page 60: Refactoring Legacy Code - true story

Data-driven tests

Page 61: Refactoring Legacy Code - true story

ProductService from…

Page 62: Refactoring Legacy Code - true story

… to

• Private method, thus no tests for this.• How am I sure this works?

• I am not. Now. I was pretty sure. I think. Hope.

Page 63: Refactoring Legacy Code - true story

AttachmentService from…

Page 64: Refactoring Legacy Code - true story

… to

Page 65: Refactoring Legacy Code - true story

Step 3

Work with attachment directory/name

Page 66: Refactoring Legacy Code - true story

Things not to do

• Have a constructor to throw an Exception.

• How would I change this now:• Factory to create the directory• Create AttachmentDirectory only if dir exists

• ready?

Page 67: Refactoring Legacy Code - true story

AttachmentDirectory

Page 68: Refactoring Legacy Code - true story

AttachmentDirFactory

Page 69: Refactoring Legacy Code - true story

AttDirFactoryTest

Page 70: Refactoring Legacy Code - true story

AttachmentFile(Factory)

• Similar concept with AttachmentFile and AttachmentFileFactory

• With one difference• AttachmentFile is interface and has two concrete

classes PlainAttachmentFile and WebImageAttachmentFile

• Use of Template-pattern (which changed a lot later)

Page 71: Refactoring Legacy Code - true story

AttachmentFileTemplTest

Page 72: Refactoring Legacy Code - true story

AttachmentFileTemplatestarting to get messy

Page 73: Refactoring Legacy Code - true story

verify: AttachmentService

Page 74: Refactoring Legacy Code - true story

Step 4

do similar changes to AttachmentService#moveToAttachmentDir

Page 75: Refactoring Legacy Code - true story

AttachmentFile

Page 76: Refactoring Legacy Code - true story

AttachmentFile

• Too many responsibilities.• Would turn up with lot of methods. I needed to

do something.• Did try to not to repeat myself (not shown – ask

for more info later)

Page 77: Refactoring Legacy Code - true story

The first draft

Page 78: Refactoring Legacy Code - true story

Step 5

Copying and moving files

Page 79: Refactoring Legacy Code - true story

>150 LOC @ AttachmentService

Page 80: Refactoring Legacy Code - true story

… to 4*4

Page 81: Refactoring Legacy Code - true story

The classes within the process

Page 82: Refactoring Legacy Code - true story

Steps

• Factory to create an AttachmentFile• Factory creates a AttachmentFileProcess –

process to move/copy/clone/delete the AttachmentFile

• For specific AttachmentFileType, it does different things

• The execute method takes one argument, AttachmentFileAction, which either is move/copy/clone/delete/rename

Page 83: Refactoring Legacy Code - true story

Some steps

Page 84: Refactoring Legacy Code - true story

AttchmntFileProcessFactory

Page 85: Refactoring Legacy Code - true story

PlainAttachmentFileProcess

Page 86: Refactoring Legacy Code - true story

ASpecificImageProcess

Page 87: Refactoring Legacy Code - true story

ASpecificImageProcess

Page 88: Refactoring Legacy Code - true story

FileCopyAction

Page 89: Refactoring Legacy Code - true story

FileDeleteAction

Page 90: Refactoring Legacy Code - true story

A mysterious test

Page 91: Refactoring Legacy Code - true story

Later steps

• Renaming classes, reordering packages• Using template

Page 92: Refactoring Legacy Code - true story

One last thing – listen the tests

Page 93: Refactoring Legacy Code - true story

The difference:

Page 94: Refactoring Legacy Code - true story

And that led to

• New class: AttachmentFileName• A domain logic for handling name of the Attachment

• I’m working with this. Now.

Page 95: Refactoring Legacy Code - true story

Learnings

• Slow to refactor• Test-Driven design can work even in brown-field

projects• Integration to old system required integration tests

• How valuable the changes were to the customer• With my current understanding:

• Split the changes to two – deploy both separately.

Page 96: Refactoring Legacy Code - true story

Tests

0

50

100

150

200

250

PassedFailed

Page 97: Refactoring Legacy Code - true story

Questions?

Page 98: Refactoring Legacy Code - true story

Puhelin: +358 50 341 5620

email: [email protected] / [email protected]

Twitter: @rinkkasatiainen

Aki Salmi