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

Down the Rabbit Hole: An Adventure in Legacy Code

Down the Rabbit Hole: An Adventure in Legacy Code

Legacy code is unpredictable & difficult to understand. Most of us will work with legacy code, so it’s important for us to understand its major pain points & their underlying causes. You’ ll come away from this talk with practical strategies to help you understand, improve, & even love legacy code.

Loren Crawford

April 17, 2018
Tweet

Other Decks in Technology

Transcript

  1. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  2. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  3. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  4. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  5. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  6. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  7. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  8. “When someone makes fun of legacy code remember legacy is

    a funny way to spell successful. Your startup with perfect code with fail.” Dustin Shields-Cloues what’s up with legacy code?
  9. legacy code is a system of working software written by

    many developers over time that is often hard to change & contains business logic complexities that are not easily distilled from the code* * my working definition what’s up with legacy code?
  10. Users want to be able to search online sellers for

    Alpaca yarn. add a feature adding a feature
  11. where do I test the thing? - What class am

    I calling in the test? - What type of object am I using? - What is the expected behavior? adding a feature
  12. “it’s better to confront the beast than to hide from

    it.” Michael Feathers ⃟ Working Effectively with Legacy Code adding a feature
  13. Prove code works & your assumptions are correct. Uncover trivial

    bugs. Document how code actually works. adding a feature
  14. Prove code works & your assumptions are correct. Uncover trivial

    bugs. Document how code actually works. Help design better code. adding a feature
  15. Working Effectively with Legacy Code Michael Feathers Chapter 6 I

    Don’t Have Much Time and I Have to Change It adding a feature
  16. Users want to be able to search online sellers for

    Alpaca yarn. add a feature adding a feature Must also be fair trade & organic.
  17. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: "wool"}) found_sellers << sellers end found_sellers.flatten! preferred_sellers = [] mid_sellers = [] found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers elsif return found_sellers end adding a feature Don’t try to read this
  18. when "cotton" ... when "silk" ... when "mohair" ... when

    "cashmere" ... when "synthetic" ... when "angora" ... else ... adding a feature
  19. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  20. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  21. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  22. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  23. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  24. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  25. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  26. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  27. RSpec.describe Yarn, "#get_seller" do context "when you pass in wool

    as yarn_name" do it "returns one found seller" do ... end end end adding a feature Test
  28. it "returns one found seller" do seller = Seller.create(name: "Get

    Stitching", is_organic: true) ... end adding a feature Test
  29. it "returns one found seller" do seller = Seller.create(name: "Get

    Stitching", is_organic: true, is_fair_trade: true) ... end adding a feature Test
  30. it "returns one found seller" do seller = Seller.create(name: "Get

    Stitching", is_organic: true, is_fair_trade: true, in_good_standing: true) ... end adding a feature Test
  31. it "returns one found seller" do seller = Seller.create(name: "Get

    Stitching", is_organic: true, is_fair_trade: true, in_good_standing: true) yarn = Yarn.create(name: "wool", seller: seller) ... end adding a feature Test
  32. it "returns one found seller" do seller = Seller.create(name: "Get

    Stitching", is_organic: true, is_fair_trade: true, in_good_standing: true) yarn = Yarn.create(name: "wool", seller: seller, is_organic:true) ... end adding a feature Test
  33. it "returns one found seller" do seller = Seller.create(name: "Get

    Stitching", is_organic: true, is_fair_trade: true, in_good_standing: true) yarn = Yarn.create(name: "wool", seller: seller, is_organic:true, is_fair_trade: true) ... end adding a feature Test
  34. it "returns two found sellers" do ... found_sellers = Yarn.get_sellers("wool")

    expect(found_sellers.count).to eq 1 expect(found_sellers.first).to eq(seller) end adding a feature Test
  35. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  36. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  37. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" ... found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers else return found_sellers end adding a feature
  38. context "when you have more preferred sellers than mid_sellers" do

    it "returns an array with preferred sellers before mid_sellers" do ... end end adding a feature Test
  39. context "when you have more preferred sellers than mid_sellers" do

    it "returns an array with preferred sellers and mid_sellers" do preferred_seller_1 = Seller.create(name: "Preferred seller 1", is_organic: true, is_fair_trade: true) preferred_seller_2 = Seller.create(name: "Preferred seller 2", is_organic: true, is_fair_trade: true) mid_seller = Seller.create(name: "mid seller", is_organic: true, is_fair_trade: false) end end adding a feature Test
  40. context "when you have more preferred sellers than mid_sellers" do

    it "returns an array with preferred sellers and mid_sellers" do preferred_seller_1 = Seller.create(name: "Preferred seller 1", is_organic: true, is_fair_trade: true) preferred_seller_2 = Seller.create(name: "Preferred seller 2", is_organic: true, is_fair_trade: true) mid_seller = Seller.create(name: "mid seller", is_organic: true, is_fair_trade: false) end end adding a feature Test
  41. it "returns an array with preferred sellers and mid_sellers" do

    preferred_seller_1 = Seller.create(name: "Preferred seller 1", is_organic: true, is_fair_trade: true) preferred_seller_2 = Seller.create(name: "Preferred seller 2", is_organic: true, is_fair_trade: true) mid_seller = Seller.create(name: "mid seller", is_organic: true, is_fair_trade: false) end end adding a feature Test
  42. context "when you have more preferred sellers than mid_sellers" do

    it "returns an array with preferred sellers and mid_sellers" do preferred_seller_1 = ... preferred_seller_2 = ... mid_seller = ... Yarn.create(name: "wool", seller: preferred_seller_1, ...) Yarn.create(name: "wool", seller: preferred_seller_2, ...) Yarn.create(name: "wool", seller: mid_seller, ...) end end adding a feature Test
  43. context "when you have more preferred sellers than mid_sellers" do

    it "returns an array with preferred sellers and mid_sellers" do // setup found_sellers = Yarn.get_sellers("wool") end end adding a feature Test
  44. context "when you have more preferred sellers than mid_sellers" do

    it "returns an array with preferred sellers and mid_sellers" do // setup found_sellers = Yarn.get_sellers("wool") expect(found_sellers.count).to eq 3 expect(found_sellers.last).to eq(mid_seller) end end adding a feature Test
  45. “Whenever I do a refactoring, the first step is always

    the same. I need to build a solid set of tests for that section of code. The tests are essential because even though I following refactorings structured to avoid most of the opportunities for introducing bugs, I’m still human and still make mistakes.” Martin Fowler Refactoring: Improving the Design of Existing Code
  46. Users want to be able to search online sellers for

    Alpaca yarn. add a feature adding a feature
  47. context "when you pass in alpaca as yarn_name" do it

    "returns sellers with only yarn that is fair trade and organic" do end end add a feature adding a feature Test
  48. context "when you pass in alpaca as yarn_name" do it

    "returns sellers with only yarn that is fair trade and organic" do seller = Seller.create(name: "Alpaca good seller", is_organic: true, is_fair_trade: true) not_included_seller = Seller.create(name: "Alpaca bad seller", is_organic: false, is_fair_trade: false) end end add a feature adding a feature Test
  49. context "when you pass in alpaca as yarn_name" do it

    "returns sellers with only yarn that is fair trade and organic" do seller = Seller.create(name: "Alpaca good seller", is_organic: true, is_fair_trade: true) not_included_seller = Seller.create(name: "Alpaca bad seller", is_organic: false, is_fair_trade: false) end end adding a feature Test
  50. context "when you pass in alpaca as yarn_name" do it

    "returns sellers with only yarn that is fair trade and organic" do seller = Seller.create(...) not_included_seller = Seller.create(...) Yarn.create(name: "alpaca", seller: seller, ...) Yarn.create(name: "alpaca", seller: not_included_seller, ...) end end add a feature adding a feature Test
  51. context "when you pass in alpaca as yarn_name" do it

    "returns sellers with only yarn that is fair trade and organic" do // setup found_sellers = Yarn.get_sellers("alpaca") end end add a feature adding a feature Test
  52. context "when you pass in alpaca as yarn_name" do it

    "returns sellers with only yarn that is fair trade and organic" do // setup found_sellers = Yarn.get_sellers("alpaca") expect(found_sellers.count).to eq 1 expect(found_sellers.first).to eq(seller) end end add a feature adding a feature Test
  53. when "alpaca" sellers = Seller.joins(:yarn).where(yarns: {name: "alpaca"}) found_sellers = []

    sellers.each do |seller| ... end add a feature adding a feature
  54. when "alpaca" sellers = Seller.joins(:yarn).where(yarns: {name: "alpaca"}) found_sellers = []

    sellers.each do |seller| if(seller.is_organic? && seller.is_fair_trade?) ... end end add a feature adding a feature
  55. when "alpaca" sellers = Seller.joins(:yarn).where(yarns: {name: "alpaca"}) found_sellers = []

    sellers.each do |seller| if(seller.is_organic? && seller.is_fair_trade?) found_sellers << seller end end return found_sellers add a feature adding a feature
  56. when "alpaca" sellers = Seller.joins(:yarn).where(yarns: {name: "alpaca"}) found_sellers = []

    sellers.each do |seller| if(seller.is_organic? && seller.is_fair_trade?) found_sellers << seller end end return found_sellers add a feature adding a feature
  57. class Yarn < ApplicationRecord ... def self.get_sellers(name, gauge=nil, color=nil, weight=nil)

    found_sellers = [] case name when "wool" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "wool", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: "wool"}) found_sellers << sellers end found_sellers.flatten! preferred_sellers = [] mid_sellers = [] found_sellers.each do |seller| if(seller.is_in_good_standing?) if(seller.is_organic? && seller.is_fair_trade?) preferred_sellers << seller elsif (seller.is_organic? && !seller.is_fair_trade?) mid_sellers << seller end end end if preferred_sellers.count > mid_sellers.count return preferred_sellers + mid_sellers elsif return found_sellers end adding a feature Don’t try to read this
  58. class Yarn def self.get_sellers(name, gauge=nil, color=nil, weight=nil) found_sellers = []

    case name when "wool" ... when "cashmere" ... when "cotton" ... when "alpaca" ... when "silk" ... when "synthetic" ... Good opportunity to replace conditional with polymorphism adding a feature
  59. class Yarn def self.get_sellers(name, gauge=nil, color=nil, weight=nil) found_sellers = []

    case name when "wool" ... when "cashmere" ... when "cotton" ... when "alpaca" ... when "silk" ... when "synthetic" ... Good opportunity to replace conditional with subclasses adding a feature
  60. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  61. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  62. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  63. context "when you pass in silk as yarn_name" do it

    "returns sellers with silk and silk blend yarns" do ... end end fixing a bug Test
  64. context "when you pass in silk as yarn_name" do it

    "returns sellers with silk and silk blend yarns" do seller = Seller.create(name: "Silk seller", is_organic: true) silk_blend_seller = Seller.create(name: "Silk blend seller", is_organic: true,) end end fixing a bug Test
  65. context "when you pass in silk as yarn_name" do it

    "returns sellers with silk and silk blend yarns" do seller = ... silk_blend_seller = ... Yarn.create(name: "silk", seller: seller, is_organic:true, ..) Yarn.create(name: "silk-blend", seller: silk_blend_seller, is_organic:true,...) end end fixing a bug Test
  66. context "when you pass in silk as yarn_name" do it

    "returns sellers with silk and silk blend yarns" do seller = ... silk_blend_seller = ... Yarn.create(name: "silk", seller: seller, is_organic:true, ..) Yarn.create(name: "silk-blend", seller: silk_blend_seller, is_organic:true,...) end end fixing a bug Test
  67. context "when you pass in silk as yarn_name" do it

    "returns sellers with silk and silk blend yarns" do //setup found_sellers = Yarn.get_sellers("silk") end end fixing a bug Test
  68. context "when you pass in silk as yarn_name" do it

    "returns sellers with silk and silk blend yarns" do //setup found_sellers = Yarn.get_sellers("silk") expect(found_sellers.count).to eq 2 expect(found_sellers).to include(silk_blend_seller) end end fixing a bug Test
  69. Yarn#get_seller when you pass in silk as yarn_name returns sellers

    with silk and silk blend yarns Failure/Error: expect(found_sellers.count).to eq 2 expected: 2 got: 1 (compared using ==) add a feature fixing a bug
  70. when "silk" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns:

    { name: "silk", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: ["silk", "silk-blend"]}) found_sellers << sellers end fixing a bug
  71. when "silk" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns:

    { name: "silk", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: ["silk", "silk-blend"]}) found_sellers << sellers end fixing a bug
  72. when "silk" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns:

    { name: "silk", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: ["silk", "silk-blend"]}) found_sellers << sellers end fixing a bug
  73. when "silk" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns:

    { name: "silk", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: ["silk", "silk-blend"]}) found_sellers << sellers end fixing a bug
  74. when "silk" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns:

    { name: "silk", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: ["silk", "silk-blend"]}) found_sellers << sellers end fixing a bug
  75. when "silk" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns:

    { name: "silk", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: ["silk", "silk-blend"]}) found_sellers << sellers end fixing a bug
  76. when "silk" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns:

    { name: "silk", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: ["silk", "silk-blend"]}) found_sellers << sellers end fixing a bug
  77. when "silk" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns:

    { name: "silk", gauge: gauge }) found_sellers << sellers elsif(color && !gauge && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color }) found_sellers << sellers elsif(weight && !gauge && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: weight }) found_sellers << sellers elsif(gauge && color && !weight) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color:color }) found_sellers << sellers elsif(gauge && weight && !color) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, weight:weight }) found_sellers << sellers elsif (color && weight && !gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", color: color, weight: weight }) found_sellers << sellers elsif (color && weight && gauge) sellers = Seller.joins(:yarn).where(yarns: { name: "silk", gauge: gauge, color: color, weight: weight }) found_sellers << sellers else sellers = Seller.joins(:yarn).where(yarns: {name: ["silk", "silk-blend"]}) found_sellers << sellers end fixing a bug
  78. when "silk" if(gauge && !color && !weight) sellers = Seller.joins(:yarn).where(yarns:

    { name: "silk", gauge: gauge }) found_sellers << sellers ... fixing a bug
  79. context "when you pass in silk as yarn_name" do it

    "returns sellers with silk and silk blend yarns" do seller = ... silk_blend_seller = ... Yarn.create(name: "silk", seller: seller, is_organic:true, gauge: 6, ...) Yarn.create(name: "silk-blend", seller: silk_blend_seller, is_organic:true, gauge: 6, ...) end end fixing a bug Test
  80. context "when you pass in silk as yarn_name" do it

    "returns sellers with silk and silk blend yarns" do //setup found_sellers = Yarn.get_sellers("silk", 6) expect(found_sellers.count).to eq 2 expect(found_sellers).to include(silk_blend_seller) end end fixing a bug Test
  81. Yarn#get_seller when you pass in silk as yarn_name returns sellers

    with silk and silk blend yarns Failure/Error: expect(found_sellers.count).to eq 2 expected: 2 got: 1 (compared using ==) add a feature fixing a bug
  82. when "silk" ... sellers = Seller.joins(:yarn).where(yarns: { name: ["silk", "silk-blend"],

    gauge: gauge }) found_sellers << sellers end fixing a bug
  83. Table of Contents 1. What’s up with legacy code? 2.

    Pain points a. Adding a Feature b. Fixing a Bug 3. Lessons Learned 4. Other recommendations
  84. lessons learned add tests when you touch code that is

    currently untested. BONUS: THIS WILL HELP YOU LEARN QUICKLY THINGS ABOUT HOW YOUR APP WORKS
  85. other recommendations Refactoring: Improving the Design of Existing Code Martin

    Fowler Working Effectively With Legacy Code Michael C. Feathers Object Oriented Design In Ruby Sandi Metz Apprenticeship Patterns Adewale Oshineye and Dave Hoover A Common Taxonomy of Bugs and How to Catch Them Kylie Stradley Get a Whiff of This Sandi Metz https://sourcemaking.com/refactoring/smells/