Working Effectively With Legacy Code

Book notes of Working Effectively With Legacy Code

Get Started. It's Free
or sign up with your email address
Working Effectively With Legacy Code by Mind Map: Working Effectively With Legacy Code

1. Adding a feature

1.1. Test Driven Development

1.1.1. Write a failing test

1.1.2. Get it to compile

1.1.3. Make it pass

1.1.4. Remove Duplication

1.1.4.1. Refactor

1.1.5. Repeat

1.1.6. Legacy Addition

1.1.6.1. Get the class under test

1.2. Inheritance

1.2.1. Can be useful but use carefully

1.2.1.1. long term could cause confusion & need refactoring

1.2.1.2. Liskov substitution principle

1.2.1.2.1. A Rectangle != Square

1.2.1.2.2. Objects of subclasses should be substitutable for their parent class

1.2.2. Avoid overwriting concrete classes

1.2.2.1. if so, try to call super

2. Test Code

2.1. Not production code

2.1.1. Mock objects can break encapsulation if it makes testing easier

2.1.2. Try passing null

2.1.2.1. Worst case... exception

2.1.2.2. Maybe implement Null Object Pattern

2.1.2.2.1. This can be production code

3. Legacy

3.1. Dilema

3.1.1. When we change code - have tests, to add tests, I need to change code

3.2. Definition

3.2.1. Code Without Tests

4. Unit Tests

4.1. FAST!

4.2. Localize problems

4.2.1. quickly

4.3. NOT

4.3.1. touch database

4.3.2. Communicate across network

4.3.3. touch file system

4.3.4. require special environment config

4.3.5. Integration tests

5. Refactoring

5.1. Definition

5.1.1. Changes made to structure w/out changing behavior to make it easier to understand

5.2. Initially

5.2.1. Start Ugly

5.2.2. Surgery analogy

5.2.2.1. Scar left after

5.2.2.1.1. BUT

5.3. Legacy Code Change Algorithm

5.3.1. Identify Change Points

5.3.2. Find Test Points

5.3.3. Break Dependencies

5.3.3.1. interfaces

5.3.4. Write Tests

5.3.5. Make Changes & Refactor

6. Adding Behavior

6.1. Sprout Method

6.1.1. Put behavior in a new, distinct, testable method

6.1.2. Test this method even if/though caller is untested

6.1.3. Worst Case

6.1.3.1. Static method w/ arguments of intended class

6.1.3.1.1. This is a scar

6.1.3.1.2. Over time it will find a home

6.2. Sprout Class

6.2.1. When difficult to instantiate a class for testing

6.2.2. New class to do new behavior

6.2.3. Would NOT receive "Caller Class" (untested class where code was desired to be put in the first place)

6.2.3.1. Caller Class = Source Class

6.3. Wrap Method

6.3.1. Wrap source method with simpler delegating method

6.3.1.1. Or reuse method name & abstract original code

6.3.2. Decouple functionality from being shoved into the same method

6.3.3. Prevents temporal coupling

6.4. Wrap Class

6.4.1. Decorator Pattern

7. Testing takes too long?

7.1. When we don't test it takes too long

7.1.1. Compile Time

7.1.1.1. Entire project compilation

7.1.2. Deploy Time

7.1.3. Manual Verification

7.1.3.1. Multiple Times

7.2. Testing

7.2.1. Instantaneous feedback

7.2.2. Helps keep you in context & focused

7.2.3. Future guarantee of the feature with no additional work afterward

7.3. Interfaces require less code to need compiling

7.3.1. so utilize them

8. Getting Classes into Test Harness

8.1. Extract Interface

8.1.1. Mock out new interfaces

8.2. Hidden dependencies

8.2.1. Parameterized constructor

8.2.2. Supersede Instance Variable

8.2.2.1. Worst Case Scenario

8.2.2.2. Public Setter

8.3. Global Dependencies

8.3.1. Singletons

8.3.1.1. Relax it

8.3.1.1.1. Introduce static setter

8.4. Subclass

8.4.1. when difficult to construct, dependencies can be overwritten by a subclass

8.4.1.1. used as an interface to test desirable testable code

9. Get Method into Test Harness

9.1. Private method

9.1.1. test appropriate public methods

9.1.1.1. they will touch private methods, no need to test the private ones specifically

9.1.2. Make it public

9.1.2.1. if bad

9.1.2.1.1. fix class

9.1.3. Make it protected & subclass with a public method

9.1.3.1. java package level scope to circumvent the subclass

9.1.4. Reflection

9.1.4.1. Try to NEVER do this

9.1.4.1.1. sets bad presedence

9.1.4.1.2. can mask how bad a class is

10. Finding Bugs

10.1. Tests prevent future bugs

10.2. Once reducing the area a bug is in

10.2.1. write

10.2.1.1. Characterization Tests

10.2.1.1.1. Tests what the code does, not supposed to do

10.2.1.1.2. Ensures you are not breaking current working functionality

10.2.1.1.3. Steps

11. Adding Tests

11.1. Seam

11.1.1. Definition

11.1.1.1. Alter behavior in program without editing in that place

11.1.1.1.1. Test & Prod code IS THE SAME CODE

11.1.1.2. enabling point

11.1.1.2.1. spot to enable seam

11.1.2. Use indirect / hard seams to change legacy code very little so you can get tests in place

11.1.2.1. Then refactor

11.1.3. Types

11.1.3.1. Object Seam

11.1.3.1.1. Replace / Overwrite Object

11.1.3.2. Pre-Processing Seam

11.1.3.2.1. Compiler arguments to overwrite function definitions

11.1.3.3. Link Seam

11.1.3.3.1. Overwrite 'Linked'" / imported objects

11.1.3.3.2. Java

12. Change Definition

12.1. Does it change behavior??

12.2. Functionality

12.3. New Functionality

12.4. Structure

12.5. Resource Usage