Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Less Painful Legacy Code Replacement

Avatar for jtu0 jtu0
June 21, 2016

Less Painful Legacy Code Replacement

Replacing legacy code is a challenge on every front, from managing stakeholder expectations to tackling the technical work. Thoughtful preparation and a pocket full of tools can make the experience a little less painful.

45min talk at Open Source Bridge 2016:
http://opensourcebridge.org/sessions/1746

20min talk at SF.rb meetup March 2016:
http://www.meetup.com/sf-dot-rb/events/229451240/

Contact me if you would like me to speak at your conference.

Avatar for jtu0

jtu0

June 21, 2016
Tweet

Other Decks in Programming

Transcript

  1. Photo credit: Johannes Bader, via Flickr @ https://www.flickr. com/photos/johannesbader/ CC

    BY-SA 2.0 Image overlaid second image • Can you add this feature? Totally reasonable requests
  2. Photo credit: Erik, via Flickr @ https://www.flickr. com/photos/zebrapares/4529836138 CC BY-NC-SA

    2.0 Image reversed and cropped Totally reasonable requests • Can you add this feature? • Can you get rid of this timeout?
  3. • Select Your Option • Decision Factors • Tools for

    Rewriting • Design Patterns Outline
  4. Select Your Option 1) Small change if foo … elsif

    bar … elsif baz … elsif YOUR_CHANGE_HERE # tiny change else … end Photo credit: adam nicholson, via Flickr @ https://www.flickr. com/photos/adamgn/5434050218/ CC BY 3.0
  5. class Tree attr_accessor :fruits def grow_fruits create_stem @fruits << Apple.new

    create_stem @fruits << Orange.new end end > my_tree = Tree.new
  6. class Tree attr_accessor :fruits def grow_fruits create_stem @fruits << Apple.new

    create_stem @fruits << Orange.new end end > my_tree = Tree.new > my_tree.grow_fruits
  7. class Tree attr_accessor :fruits def grow_fruits create_stem @fruits << Apple.new

    create_stem @fruits << Orange.new end def add_new_fruit(fruit:) create_stem @fruits << fruit end end
  8. class Tree attr_accessor :fruits def grow_fruits create_stem @fruits << Apple.new

    create_stem @fruits << Orange.new end def add_new_fruit(fruit:) create_stem @fruits << fruit end end
  9. class Tree attr_accessor :fruits def grow_fruits add_new_fruit(fruit: Apple.new) create_stem @fruits

    << Orange.new end def add_new_fruit(fruit:) create_stem @fruits << fruit end end
  10. class Tree attr_accessor :fruits def grow_fruits add_new_fruit(fruit: Apple.new) create_stem @fruits

    << Orange.new end def add_new_fruit(fruit:) create_stem @fruits << fruit end end
  11. class Tree attr_accessor :fruits def grow_fruits ... end def add_new_fruit(fruit)

    ... end end > my_tree = Tree.new > my_tree.grow_fruits
  12. class Tree attr_accessor :fruits def grow_fruits create_stem @fruits << Apple.new

    create_stem @fruits << Orange.new end end > my_tree = Tree.new > my_tree.grow_fruits
  13. class Tree attr_accessor :fruits def grow_fruits add_new_fruit(fruit: Apple.new) add_new_fruit(fruit: Orange.new)

    add_new_fruit(fruit: Banana.new) end def add_new_fruit(fruit:) create_stem @fruits << fruit end end
  14. Refactor is • Incremental change • No behavior change •

    Tests also don’t change (and still pass) • Easier to modify code (add_new_fruit(fruit: Banana.new)) Refactor vs Rewrite
  15. Refactor vs Rewrite Rewrite dangers: • More opportunities to miss

    requirements • Longer time • All or nothing
  16. • Choose refactor whenever possible • Why do you want

    to rewrite? • Are you actually rewriting while saying you’re refactoring? Refactor vs Rewrite
  17. • Select Your Option (Simple / Refactor / Rewrite) •

    Decision Factors • Tools for Rewriting • Design Patterns Outline
  18. Case Study An existing appointment searcher finds available appointment inventory,

    so patients can see doctors Improve performance so it stops hitting timeouts
  19. 1. Go to the model (AppointmentInventory) 2. Build up a

    giant SQL statement based on parameters 3. Add or remove rows based on business rules 4. Repeat steps 1-3 for all the other business rules 5. Return results class LegacySearcher end
  20. Decision Factors: Goals Some possible goals: • Add a feature

    • Fix a bug • Improve performance • Upgrade external dependency / add security
  21. 1. Go to the model (AppointmentInventory) 2. Build up a

    giant SQL statement based on parameters 3. Add or remove rows based on business rules 4. Repeat steps 1-3 for all the other business rules 5. Return results class LegacySearcher end
  22. 1. Go to the model (AppointmentInventory) 2. Build up a

    giant SQL statement based on parameters 3. Add or remove rows based on business rules 4. Repeat steps 1-3 for all the other business rules 5. Return results class LegacySearcher end
  23. 1. Go to the model (AppointmentInventory) 2. Build up a

    giant SQL statement based on parameters 3. Add or remove rows based on business rules 4. Repeat steps 1-3 for all the other business rules 5. Return results class LegacySearcher end
  24. 1. Go to the model (AppointmentInventory) 2. Build up a

    giant SQL statement based on parameters 3. Add or remove rows based on business rules 4. Repeat steps 1-3 for all the other business rules 5. Return results class LegacySearcher end
  25. class LegacySearcher end 1. Go to the model (AppointmentInventory) 2.

    Build up a giant SQL statement based on parameters 3. Add or remove rows from results 4. Repeat steps 1-3 for all the other business rules 5. Return results
  26. Decision Factors: Risks How much risk is acceptable? • Business

    • Reputation internally • User pain • Accuracy
  27. Decision Factors: Tests • Legacy test suite? Useful? • Does

    anyone know what this is supposed to do?
  28. Decision Factors: Tests • Legacy test suite? Useful? • Does

    anyone know what this is supposed to do?
  29. • Select Your Option (Simple / Refactor / Rewrite) •

    Decision Factors (Goals / Risks / Tests) • Tools for Rewriting ◦ Team Commitment ◦ Stabilize Before Extending ◦ Scientist ◦ Feature Flags & Beta Users ◦ Tests! • Design Patterns Outline
  30. • Select Your Option (Simple / Refactor / Rewrite) •

    Decision Factors (Goals / Risks / Tests) • Tools for Rewriting (Team / Stabilize / Scientist / Beta Users / Tests) • Design Patterns Outline
  31. 1) Strangler Application (Martin Fowler) 2) Sprout Method (Michael Feathers)

    3) Wrap Method (Michael Feathers) Design patterns
  32. “Gradually create a new system around the edges of the

    old, letting it grow slowly over several years until the old system is strangled.” 1) Strangler Application (Martin Fowler) Design patterns
  33. Design Patterns 1) Strangler Application (Martin Fowler) 2) Sprout Method

    (Michael Feathers) • Add call to new method • Test new method • New method is no longer legacy
  34. Design Patterns 1) Strangler Application (Martin Fowler) 2) Sprout Method

    (Michael Feathers) 3) Wrap Method (Michael Feathers)
  35. Design Patterns 1) Strangler Application 2) Sprout Method 3) Wrap

    Method • Wrap legacy class in new class, slowly cut over • New class is no longer legacy • AKA Decorator Pattern
  36. class AppointmentsController def search Searcher.new(params).results end end class Searcher def

    results if $feature_flag NewSearcher.new.results else LegacySearcher.new(params).results end end end New class Original legacy class
  37. Design Patterns 1) Strangler Application (Martin Fowler) 2) Sprout Method

    (Michael Feathers) 3) Wrap Method (Michael Feathers) ? Sprout vs Wrap
  38. • Select Your Option (Simple / Refactor / Rewrite) •

    Decision Factors (Goals / Risks / Tests) • Tools for Rewriting (Team / Stabilize / Scientist / Beta Users / Tests) • Design Patterns (Strangler / Sprout / Wrap) Outline