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

How to work with
 Legacy (Rails) Code

How to work with
 Legacy (Rails) Code

Why does software become “legacy”?

Many of us have tried to discover ways to prevent code from becoming legacy.

We tried and had good intentions.
We've written books on principles, best practices and design patterns that can help programmers keep their systems clean.
But even the most disciplined will create a mess from time to time.

This talk will cover the main problems, that lead to legacy code and provide some guidance, tools and patterns, 
which can help you to work with legacy code and how to avoid it the next time.

DonSchado

May 24, 2016
Tweet

More Decks by DonSchado

Other Decks in Programming

Transcript

  1. Railslove We love to build the web. How to work

    with
 Legacy (Rails) Code Marco Schaden
 @donschado [email protected]
 24.05.2016
  2. Legacy (Rails) Code 24.05.2016 The following is a summary of

    our own practical experiences enriched with opinions and knowledge of various esteemed experts and super heroes such as Kent Beck Martin Fowler Uncle Bob Martin Michael C. Feathers Katrina Owen Sandi Metz Corey Haines Jay Fields and other respected people Disclaimer
  3. Legacy (Rails) Code 24.05.2016 Disclaimer The slides are primarily made

    with Ruby/JS web developers in mind, but apply in most principles to any language.
 We will see almost no code, but discuss a lot of best practices. You'll understand that I'm not able to show any client code. But I don't need to show you crappy code so that we can have fun about it, everybody wrote crappy code… You will have a lot of homework after this talk :)
  4. Legacy (Rails) Code 24.05.2016 Disclaimer If you don’t understand something,

    don’t hesitate to ask. Slides are available online: https://speakerdeck.com/donschado
  5. Legacy (Rails) Code 24.05.2016 Is it the customers fault
 by

    changing the requirements all the time?
  6. Legacy Code 21.10.2016 Why couldn't the customer just be happy,

    with what they first said they need…
  7. google: IT+Project+Tree How the customer 
 explained it How the

    Project Leader 
 understood it How the Analyst designed it 
 How the Programmer 
 wrote it How the Business Consultant described it How the project was 
 documented What Operations
 deployed How the customer was billed How it was supported What the customer really needed
  8. Legacy Code 21.10.2016 Random developer: 
 “It was not designed

    to do this, we need to start from scratch..."
  9. Legacy Code 21.10.2016 Surprise: Requirements Change, all the time! Software

    that cannot tolerate this, is simply poor designed.
  10. T We’re an agile team 
 and develop products for

    the web. Web applications are more than our daily business. We support our customers in close cooperation from the initial idea through to the launch - and beyond.
  11. Legacy (Rails) Code 24.05.2016 I know this is old, but

    when you remember web development in 2005, the demo is still awesome.
  12. Legacy (Rails) Code 24.05.2016 Ruby A PROGRAMMER’S BEST FRIEND TL;DR

    Ruby Yukihiro Matsumoto (Matz) https://www.ruby-lang.org Ruby is designed to be human-oriented. It reduces the burden of programming and tries to push jobs back to machines. You can accomplish more tasks with less work, in smaller yet readable code. First appeared 1995 21 years ago Multi-paradigm: 
 Object-oriented imperative functional reflective dynamically typed
  13. Legacy (Rails) Code 24.05.2016 David Heinemeier Hansson (DHH) Rails is

    an attempt to mold the beauty and productiveness of Ruby into a framework for web applications. • convention over configuration • don't repeat yourself (DRY) • model - view - controller architecture (MVC) TL;DR Rails https://www.rubyonrails.org
  14. This stuff is so 2005, you better use… ELIXIR PHOENIX

    GO EMBER ANGULAR GRUNT GULP ELM DOCKER REACT WEBPACK BROWSERIFY REDUX FLUX RUST BACKBONE MARIONETTE NODE ES2016 SASS LESS COFFEESCRIPT POLYMER KNOCKOUT METEOR HANDLEBARS EXPRESS BOWER TRAILBLAZER CLOJURE MICROSERVICES TYPESCRIPT
  15. Legacy (Rails) Code 24.05.2016 If you have lot of free

    time and are curios about new technology, it’s an amazing time to be a developer. The hype is a lie If you have a real job and maintaining a product, 
 then this doesn’t benefit you unless you rewrite your project every three months, 
 in the hottest tech available. Worse, even if you manage to resist the temptation to do that, 
 people will keep raving about their new toys and making you feel retarded.
  16. Legacy (Rails) Code 24.05.2016 Mountain West Ruby 2016 - Surviving

    the Framework Hype Cycle by Brandon Hays https://www.youtube.com/watch?v=9zc4DSTRGeM
  17. Legacy (Rails) Code 24.05.2016 </introduction> def legacy_code legacy code VS

    technical debt class You < LegacyCode::Base Change Dependencies Testing Refactoring Bugs Rewrite vs Refactoring Tools
  18. Legacy Code 21.10.2016 many “legacy” systems have reliably served 


    the needs of their businesses generating value (usually it’s the main system and the source of income…) people and processes depend on it just because code is old 
 doesn’t mean it has to be thrown away Some people in our industry call it legacy while the codebase is less than 2 years old?! #vintage #oldtimer #classic
  19. Legacy Code 21.10.2016 slang for complex, difficult to change code

    code with many dependencies mostly somebody else's code “old” code with deprecated syntax/features or style highly coupled code / “spaghetti" / ball of mud code you’re afraid to touch code without tests def legacy_code
  20. Legacy (Rails) Code 24.05.2016 The Michael C. Feathers definition: “code

    without tests” no matter how well written it is, 
 without tests you don't know 
 if a change makes it better or worse def legacy_code
  21. Legacy (Rails) Code 24.05.2016 legacy code vs technical debt *sometimes

    required to move projects forward referring to the eventual consequences of any system design
  22. Legacy (Rails) Code 24.05.2016 legacy code vs technical debt accumulating

    interest *sometimes required to move projects forward referring to the eventual consequences of any system design
  23. Legacy (Rails) Code 24.05.2016 legacy code vs technical debt accumulating

    interest *sometimes required to move projects forward referring to the eventual consequences of any system design the result of unpaid debt
  24. Legacy (Rails) Code 24.05.2016 legacy code vs technical debt accumulating

    interest *sometimes required to move projects forward referring to the eventual consequences of any system design code smells • metaprogramming madness • long parameter list • shotgun surgery • duplicated code • feature envy • large class • long method • case statements • complex conditionals the result of unpaid debt
  25. Legacy (Rails) Code 24.05.2016 legacy code vs technical debt accumulating

    interest code smells refactoring • metaprogramming madness • long parameter list • shotgun surgery • duplicated code • feature envy • large class • long method • case statements • complex conditionals *sometimes required to move projects forward referring to the eventual consequences of any system design the result of unpaid debt
  26. Legacy (Rails) Code 24.05.2016 What does crappy code say about

    the developer who wrote it? not skilled enough bad person drunk
  27. Legacy (Rails) Code 24.05.2016 What does crappy code say about

    the developer who wrote it? not skilled enough bad person drunk changing requirements tough deadline had a bad day
  28. Legacy (Rails) Code 24.05.2016 What does crappy code say about

    the developer who wrote it? not skilled enough bad person drunk changing requirements tough deadline had a bad day it's easy and funny to criticise but you might miss the context, at the time the code was written
  29. Legacy Code 21.10.2016 missing (product) strategy (no design thinking) unnecessary

    / tough deadlines unfinished requirements conceptually poor features needless complexity high fluctuation and poor on-boarding not enough training / further education doing agile wrong (yeah, we’re doing scrum/kanban we’re so agile!) The influence of management* on legacy: Even a development process that encourages code reviews and pairing
 can yield bad technical decisions which effect the whole architecture:
  30. Legacy (Rails) Code 24.05.2016 Uncle Bob: “every 5 years, double

    new programmers, with less than 5 years experience” http://blog.cleancoder.com/uncle-bob/2014/06/20/MyLawn.html
  31. Legacy (Rails) Code 24.05.2016 In legacy code, it is particularly

    hard to come up with estimates that are meaningful. even the simplest code changes take a long time to implement it seems like no amount of time will be enough to understand everything you need to do to make a change Expensive
  32. Legacy (Rails) Code 24.05.2016 How much change can you afford

    if changes are risky? What changes do we have to make? How will we know that we've done them correctly and haven't broken anything? Risk Often we don't know how much of the existing behaviour is at risk 
 when we make our changes.
  33. Legacy (Rails) Code 24.05.2016 1. Identify change points 2. Find

    test points 3. Break dependencies 4. Write tests 5. Make your changes and refactor Algorithm: Michael C. Feathers
  34. Legacy (Rails) Code 24.05.2016 1. Identify change points 2. Find

    test points 3. Break dependencies 4. Write tests 5. Make your changes and refactor Algorithm: debugger, inspect, puts, tap, raise, console.log, … most obvious impediment to testing this can be hard for side effects
 sometime requires creative and ugly technics Always leave the campground cleaner than you found it Michael C. Feathers
  35. Legacy (Rails) Code 24.05.2016 1. Identify change points 2. Find

    test points 3. Break dependencies 4. Write tests 5. Make your changes and refactor Algorithm: debugger, inspect, puts, tap, raise… most obvious impediment to testing this can be hard for side effects
 sometime requires creative and ugly technics Always leave the campground cleaner than you found it Michael C. Feathers
  36. Legacy (Rails) Code 24.05.2016 Whenever we bring up on our

    screens a nasty batch of tangled legacy code, we are experiencing the results of poor dependency management. Poor dependency management leads to code that is hard to change, fragile, and non-reusable.
 
 When classes depend directly on things that are hard to use in a test, they are hard to modify and hard to work with. Uncle Bob:
  37. Legacy (Rails) Code 24.05.2016 Dependency Inversion Principle depend on abstraction

    instead of implementation don't call 3rd Party APIs directly in Ruby you can do this implicit as a 'protocoll' 
 (we don't care for the type of the dependency)
  38. Legacy (Rails) Code 24.05.2016 Dependency Inversion Principle depend on abstraction

    instead of implementation don't call 3rd Party APIs directly in Ruby you can do this implicit as a 'protocoll' 
 (we don't care for the type of the dependency) A B before In UML the arrow points to a dependency
  39. Legacy (Rails) Code 24.05.2016 Dependency Inversion Principle depend on abstraction

    instead of implementation don't call 3rd Party APIs directly in Ruby you can do this implicit as a 'protocoll' 
 (we don't care for the type of the dependency) A B before A B after I Interface! Ohai Java! In UML the arrow points to a dependency
  40. Legacy (Rails) Code 24.05.2016 Dependency Inversion Principle Summary: DIP is

    primarily about reversing the conventional direction of dependencies from higher level components to lower level components such that lower level components are dependent upon the “interfaces” owned by the higher level components.
  41. Legacy (Rails) Code 24.05.2016 Dependency Inversion Principle Summary: DIP is

    primarily about reversing the conventional direction of dependencies from higher level components to lower level components such that lower level components are dependent upon the “interfaces” owned by the higher level components.
  42. Legacy (Rails) Code 24.05.2016 Dependency Inversion Principle Summary: DIP is

    primarily about reversing the conventional direction of dependencies from higher level components to lower level components such that lower level components are dependent upon the “interfaces” owned by the higher level components.
  43. Legacy (Rails) Code 24.05.2016 Dependency Injection class Car def start

    Engine.new.start end end class Car attr_reader :engine def initialize(engine = Engine.new) @engine = engine end def start engine.start end end before after (constructor injection) Car.new(V8Engine.new).start
  44. Legacy (Rails) Code 24.05.2016 Dependency Injection class Car def start

    Engine.new.start end end before after (setter injection) class Car attr_writer :engine def initialize end def start engine.start end def engine @engine ||= Engine.new end end
  45. Legacy (Rails) Code 24.05.2016 RubyConf 2009 - SOLID Ruby by:

    Jim Weirich https://www.youtube.com/watch?v=dKRbsE061u4 GORUCO 2009 - SOLID Object-Oriented Design by Sandi Metz https://www.youtube.com/watch?v=v-2yFMzxqwU
  46. Legacy (Rails) Code 24.05.2016 unit test <-----> integration test hard

    to localize the bug when they fail very useful in bigger refactorings many people
 prefer these
  47. Legacy (Rails) Code 24.05.2016 unit test <-----> integration test hard

    to localize the bug when they fail fast feedback very useful in bigger refactorings hard to work with
 when lots of little tests fail all the time many people
 prefer these many people
 do these wrong
  48. Legacy (Rails) Code 24.05.2016 unit test <-----> integration test hard

    to localize the bug when they fail fast feedback very useful in bigger refactorings hard to work with
 when lots of little tests fail all the time many people
 prefer these many people
 do these wrong Use high level tests only to make sure the overall system works 
 and drive the behaviour down to the unit level.
  49. Legacy (Rails) Code 24.05.2016 How to unit test In theory,

    writing a test 
 for a piece of functionality shouldn’t be too bad. We instantiate a class, 
 call its methods and check the results. What could go wrong? GOALS • comprehensive • stable • fast • few
  50. Legacy (Rails) Code 24.05.2016 The method might not be accessible

    to the test. 
 It could be private or have some other accessibility problem. It might be hard to call the method because it is hard to construct the parameters we need to call it. We might need to sense through some other object that the method uses. The method might have (bad) side effects modifying a database send notification launching a cruise missile … How to unit test GOALS • comprehensive • stable • fast • few
  51. Legacy (Rails) Code 24.05.2016 Rails Conf 2013 The Magic Tricks

    of Testing by Sandi Metz https://www.youtube.com/watch?v=URSWYvyc42M
  52. Legacy (Rails) Code 24.05.2016 Legacy Code is special Testing legacy

    code requires creative and (sometimes) ugly technics Break all rules if it saves $$$ during development
  53. Legacy (Rails) Code 24.05.2016 Rule of thumb for nearly every

    legacy system: What the system does is more important than what it is supposed to do.
  54. Legacy (Rails) Code 24.05.2016 The tests that we need when

    we want to preserve behaviour are called characterization tests.
  55. Legacy (Rails) Code 24.05.2016 def: Golden Master Testing refers to

    capturing the result of “a process”,
 and then comparing future runs against the saved golden master version to discover unexpected changes. So you expect, that what it does now, is correct.
  56. Legacy (Rails) Code 24.05.2016 1. Use a piece of untested

    
 undocumented code 2. Write an assertion 
 that you know will fail 3. Let the failure tell you 
 what the behaviour is 4. Change the test so that it 
 expects the behaviour that the code produces 5. Reason about the code 
 and exercise every branch Golden Master Testing: 6. Repeat until 
 the code is covered
  57. Legacy (Rails) Code 24.05.2016 <<< The idea is: you get

    some output and save the result (snapshot) and every time you run the test again you compare:
 
 * the same: great the test pass, because everything works as before * different: the test fails and the human has to check the output again >>>
  58. Legacy (Rails) Code 24.05.2016 This seems to be fundamentally wrong

    Are our tests really testing anything at all? What if you just characterized a bug?
  59. Legacy (Rails) Code 24.05.2016 We are trying to put in

    a mechanism to find bugs later. Protection for changing things => confidence to refactor
  60. Legacy (Rails) Code 24.05.2016 We are trying to put in

    a mechanism to find bugs later. Protection for changing things => confidence to refactor A characterization test is not a test you want to keep around. • You use the test to get coverage, • you refactor, • cleanup / write new specs • and then you throw it away!
  61. Legacy (Rails) Code 24.05.2016 Highly coupled tests will cost you

    more money than they save you Add a comment to the tests if you keep them # delete me if I fail They will fail when you need them most to pass while fixing a critical bug making an unrelated change you need to go in production NOW manually checking the output has high error potential Anti Pattern, if you keep them
  62. Legacy (Rails) Code 24.05.2016 But what about the DOM? Legacy

    JavaScript code usually couples UI and business logic
  63. Legacy (Rails) Code 24.05.2016 Get out of managing the DOM,

    as fast as you can. • test user interaction with an automated browser engine • the DOM represents the state of your application • using #ids or .classes to manipulate the DOM is dead • be careful testing markup, because it will change • use data, rel or role attributes in your tests and to describe behaviour • refactor heavy jQuery code to JavaScript objects • clear separation between UI and business logic
  64. Legacy (Rails) Code 24.05.2016 RailsConf 2014 - Bring Fun Back

    to JS: Step-by-Step Refactoring Toward Ember by Brandon Hays
 https://www.youtube.com/watch?v=VMmaKj8hCR4
  65. Legacy Code 21.10.2016 Test coverage analysis Lines of Code (no

    code is better than no code) Cyclomatic Complexity ABC Score for Code Smells Churn Afferent / Efferent Coupling Mutation Coverage Metrics: Usually language agnostic and a helpful tool, but metrics should never be a goal:
  66. Legacy (Rails) Code 24.05.2016 Ruby & Black Magic Everything is

    allowed to get to first coverage instance_variable_get(:@foo) send private / protected methods reopen class and monkey patch attr_accessor capture algorithm in a lambda and use it as assertion for new refactored code import complete staging database dump into your test env
  67. Legacy (Rails) Code 24.05.2016 Everything allowed? don’t forget to fix

    it! we can use reflection and special permissions to access private variables at runtime It is very helpful when we want to break dependencies But unless we deal with the root causes, that make these cheats necessary, we are just delaying the bill
  68. Legacy (Rails) Code 24.05.2016 When you work on legacy code,

    you will find bugs throughout the entire process. All code has bugs, usually in direct proportion to how little it is understood. Examine the possibility that someone is depending on that behaviour. Often it takes a bit of analysis to figure out how to fix a bug without causing ripple effects. fixing bugs could be dangerous... What should you do when you find a bug?
  69. Legacy (Rails) Code 24.05.2016 the list of refactoring technics is

    just the beginning composing methods (extract method, replace method with object) moving features (move method, extract class) organizing data (replace hash with object, replace type code with polymorphism) simplifying conditional (decompose, recompose, null objects) making method calls simpler (separate query from modifier, replace constructor with factory) dealing with generalization (template method, extract module, inheritance) learn when to start, when to stop when you have “refactoring tickets” you're doing it wrong (it’s part of your work!) not having the time for refactoring (because of deadlines?) is usually a sign that you need to do some refactoring Refactoring: pay your bills!
  70. Legacy (Rails) Code 24.05.2016 You are likely to see new

    possibilities, REFACTOR ALL THE CODE to pursue truth and beauty...
  71. Legacy (Rails) Code 24.05.2016 A big refactoring is a recipe

    for disaster. As ugly as the mess looks now, focus on the real problems. When you need to add a new feature: add some specs to get confidence clean up add new code refactoring hat, feature hat Don't mix an unfinished refactoring with other new tasks. Your goal is to leave the code computing exactly the same, like you found it. Always leave the campground cleaner than you found it. Discipline
  72. Legacy (Rails) Code 24.05.2016 When is it easier to start

    from scratch, instead of refactoring a big mess?
  73. Legacy (Rails) Code 24.05.2016 if it doesn't work at all

    (code has to work mostly correctly, before you refactor) if it is full of bugs and you cannot stabilize it there are big changing requirements, the current app can't support When is it easier to start from scratch, instead of refactoring a big mess? # depends… + + +
  74. Legacy Code 21.10.2016 Compromise! Refactor one large piece into components.


    Favour piece by piece rewrite over big bang rewrite and rebuild incrementally.
  75. Legacy Code 21.10.2016 Compromise! Refactor one large piece into components.


    Favour piece by piece rewrite over big bang rewrite and rebuild incrementally. • focus on the important parts • recover knowledge which got lost in complex code • preserve investment which went into the code already (time, bugfixes, requirements…)
  76. Legacy Code 21.10.2016 Compromise! Refactor one large piece into components.


    Favour piece by piece rewrite over big bang rewrite and rebuild incrementally. What's the cost of debt? Which parts are less critical? Think of extra costs for maintenance and extension caused by overly complex code. • focus on the important parts • recover knowledge which got lost in complex code • preserve investment which went into the code already (time, bugfixes, requirements…)
  77. Legacy Code 21.10.2016 TL;DR technical debt is the main reason

    for legacy code
 treat your legacy and former developers with respect
 the business always wins* (changing requirements)
 keep your code well tested
 break dependencies and decouple business logic
 maintenance == refactoring (pay your debt!)
 

  78. Legacy (Rails) Code 24.05.2016 Refactoring - Ruby Edition http://www.amazon.com/Refactoring-Ruby-Addison-Wesley-Professional/dp/0321984137 TL;DR:

    http://ghendry.net/refactor.html Patterns of Enterprise Application Architecture http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420/ Working Effectively with Legacy Code http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052
 Golden Master Testing: Refactor Complicated Views Katrina Owen: July 09, 2014 http://www.sitepoint.com/golden-master-testing-refactor-complicated-views/ Rails Conf 2013 The Magic Tricks of Testing by Sandi Metz https://www.youtube.com/watch?v=URSWYvyc42M GORUCO 2009 - SOLID Object-Oriented Design by Sandi Metz https://www.youtube.com/watch?v=v-2yFMzxqwU Rocky Mountain Ruby 2012 - Go Ahead, Make a Mess by Sandi Metz https://www.youtube.com/watch?v=f5I1iyso29U 
 RailsConf 2014 - Step-by-Step Refactoring Toward Ember by Brandon Hays
 https://www.youtube.com/watch?v=VMmaKj8hCR4 Katrina Owen - 467 tests, 0 failures, 0 confidence - Railsberry 2013 https://vimeo.com/68730418 BathRuby 2015 - Here Be Dragons, Katrina Owen https://www.youtube.com/watch?v=QAUHYzC9kFM Cascadia Ruby Conf 2012 Therapeutic Refactoring by Katrina Owen https://www.youtube.com/watch?v=J4dlF0kcThQ RailsConf 2015 - Getting a Handle on Legacy Code https://www.youtube.com/watch?v=lsFFjFp7mEE MountainWest RubyConf 2015 - Data-Driven Refactoring https://www.youtube.com/watch?v=AcdliNixNhs Baruco 2013: Design Patterns And The Proper Cultivation Thereof, Corey Haines https://www.youtube.com/watch?v=vqN3TQgsXzI Rocky Mountain Ruby 2013 SOLID and TDD, Sitting in a by Mike Nicholaides https://www.youtube.com/watch?v=FidRcixHQos RubyConf 2009 - SOLID Ruby by: Jim Weirich https://www.youtube.com/watch?v=dKRbsE061u4 content[:source]