Successfully reported this slideshow.
Your SlideShare is downloading. ×

Refactoring at Large

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
04 Advanced Javascript
04 Advanced Javascript
Loading in …3
×

Check these out next

1 of 148 Ad

Refactoring at Large

Download to read offline

Slides from my session at the Atlanta Software Craftsmanship meetup on "Refactoring at Large". Covering examples of refactoring in Java, Ruby/Rails and some ways to make architecture changes in a safer and step-by-step way

Slides from my session at the Atlanta Software Craftsmanship meetup on "Refactoring at Large". Covering examples of refactoring in Java, Ruby/Rails and some ways to make architecture changes in a safer and step-by-step way

Advertisement
Advertisement

More Related Content

Similar to Refactoring at Large (20)

Advertisement

Recently uploaded (20)

Refactoring at Large

  1. Refactoring at Large Danilo Sato - @dtsato ThoughtWorks www.dtsato.com
  2. My Plan for Today + • “Inside out” approach • Show me Code (Java) • Strategies and Objectives • Refactoring Ruby/Rails - • Larger refactorings
  3. Why do we change code?
  4. Why do we change code? • Add features
  5. Why do we change code? • Add features • Fix bugs
  6. Why do we change code? • Add features • Fix bugs • Improve design
  7. Why do we change code? • Add features • Fix bugs • Improve design • Optimization
  8. Why do we change code? • Add features • Fix bugs • Improve design • Optimization
  9. "Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior."
  10. Refactoring Code
  11. Refactoring Code
  12. Refactoring Code Extr act Method
  13. Refactoring Code
  14. Refactoring Code
  15. Refactoring Code Eliminate Duplication
  16. • Extract Method • Move Method • Extract Class • Remove Middle Man • Self Encapsulate Field • Decompose Conditional • ...
  17. Tests! Code Test
  18. Tests! Code Test
  19. Tests! Code Test Eliminate Duplication
  20. Other types... Code Test
  21. Other types... Code Code Test
  22. Other types... Code Code Test Test
  23. Other types... Code Code Test Test Split Responsibility
  24. Other types... Code Test
  25. Other types... Test Code
  26. Other types... Test Code
  27. Other types... Test Code Change Design
  28. Even more...
  29. “Big Picture”
  30. “Big Picture”
  31. “Big Picture”
  32. “Big Picture”
  33. “Big Picture”
  34. Strategy Mechanics
  35. Coding Kata!
  36. Coding Kata!
  37. CheckoutControllerTest CheckoutController Order Cart
  38. - 1 2/10/refactoring o.com/blog/2011/ http://www.dtsat / h rough-experiment st rategies-a-walkt - 0 9/01/refactoring http://paulh ammant.com/2011/ experiment CheckoutControllerTest CheckoutController Order Cart
  39. public class CheckoutControllerTest { private CheckoutController checkoutController; private Cart cart = new Cart(); // could as easily be a mock private Order order = new Order(); // could as easily be a mock private ModelMap model = new ModelMap(); @Before public void setup() { checkoutController = new CheckoutController(cart, order); } @Test public void checkout_should_add_an_upsell() { String result = checkoutController.goCheckout(model); assertThat(result, equalTo("checkout")); assertThat(model.toString(), equalTo("{cart=Cart{contents=[[iPad]]}}")); } @Test public void place_order_in_checkout_should_make_an_order() { cart.addTo("dummy"); String result = checkoutController.placeOrder(model); assertThat(result, equalTo("orderPlaced")); assertThat(model.toString(), equalTo("{order=Order{cart=Cart{contents=[[dummy]]}}}")); } }
  40. @Controller public class CheckoutController { private final Cart cart; private final Order order; @Autowired public CheckoutController(Cart cart, Order order) { this.cart = cart; this.order = order; } @RequestMapping("/go/to/checkout/") public String goCheckout(ModelMap model) { cart.addTo(findUpsell(cart)); model.addAttribute("cart", cart); return "checkout"; } @RequestMapping("/place/order") public String placeOrder(ModelMap model) { order.place(cart); model.addAttribute("order", order); return "orderPlaced"; } String findUpsell(Cart cart) { return cart.size() == 0 ? "iPad" : "iPod Nano"; } }
  41. Mechanics t his video on: riment Watch factoring-expe com/dt sato/re p://vimeo. htt
  42. First Attempt... t his video on: riment Watch factoring-expe com/dt sato/re p://vimeo. htt
  43. Strategy t his video on: riment Watch factoring-expe com/dt sato/re p://vimeo. htt
  44. Identify your Objective
  45. Identify your Objective
  46. Identify your Objective “Active” design
  47. Identify your Objective “Active” design • OO Design
  48. Identify your Objective “Active” design • OO Design • Project direction
  49. Identify your Objective “Active” design • OO Design • Project direction • Architectural decisions
  50. Identify your Objective “Passive” design
  51. Identify your Objective “Passive” design • Bad Smells
  52. Identify your Objective “Passive” design • Bad Smells • Metrics
  53. Identify your Objective “Passive” design • Bad Smells • Metrics • Visualizations
  54. Strategize
  55. Strategize ? ? ?
  56. Strategize
  57. Strategize • Isolate the impact of Change
  58. Strategize • Isolate the impact of Change • Baby Steps
  59. Strategize • Isolate the impact of Change • Baby Steps • Keep the tests green
  60. Strategize • Isolate the impact of Change • Baby Steps • Keep the tests green • Moving backwards before moving forward
  61. CalendarSearchController CalendarSearchController Test Event Event
  62. r dtsato/kata-ref actoring-calenda http ://github.com/ CalendarSearchController CalendarSearchController Test Event Event
  63. def index(params) scope = Event case params[:timeframe] when 'tomorrow' scope = scope.between_day(DateTime.now + 1) when 'this_week' scope = scope.between_dates(DateTime.now, ( DateTime.now + 6 ).end_of_day) when 'custom' if params[:start_date].blank? params[:start_date] = DateTime.now.beginning_of_week.strftime('%m/%d/%Y') end if params[:end_date].blank? params[:end_date] = (DateTime.now.end_of_week - 2).strftime('%m/%d/%Y') end scope = scope.between_dates(DateTime.strptime(params[:start_date], '%m/%d/%Y'), DateTime.strptime(params[:end_date], '%m/%d/%Y').end_of_day) when 'hour' scope = scope.between_hour_on_day(DateTime.strptime(params[:hour], '%m/%d/%Y %H: %M')) when 'today' scope = scope.between_day(DateTime.now) end @events = scope.all end
  64. def index(params) scope = Event switch/case case params[:timeframe] when 'tomorrow' scope = scope.between_day(DateTime.now + 1) when 'this_week' scope = scope.between_dates(DateTime.now, ( DateTime.now + 6 ).end_of_day) when 'custom' if params[:start_date].blank? params[:start_date] = DateTime.now.beginning_of_week.strftime('%m/%d/%Y') end if params[:end_date].blank? params[:end_date] = (DateTime.now.end_of_week - 2).strftime('%m/%d/%Y') end scope = scope.between_dates(DateTime.strptime(params[:start_date], '%m/%d/%Y'), DateTime.strptime(params[:end_date], '%m/%d/%Y').end_of_day) when 'hour' scope = scope.between_hour_on_day(DateTime.strptime(params[:hour], '%m/%d/%Y %H: %M')) when 'today' scope = scope.between_day(DateTime.now) end @events = scope.all end
  65. def index(params) scope = Event case params[:timeframe] when 'tomorrow' scope = scope.between_day(DateTime.now + 1) when 'this_week' string parsing scope = scope.between_dates(DateTime.now, ( DateTime.now + 6 ).end_of_day) when 'custom' if params[:start_date].blank? params[:start_date] = DateTime.now.beginning_of_week.strftime('%m/%d/%Y') end if params[:end_date].blank? params[:end_date] = (DateTime.now.end_of_week - 2).strftime('%m/%d/%Y') end scope = scope.between_dates(DateTime.strptime(params[:start_date], '%m/%d/%Y'), DateTime.strptime(params[:end_date], '%m/%d/%Y').end_of_day) when 'hour' scope = scope.between_hour_on_day(DateTime.strptime(params[:hour], '%m/%d/%Y %H: %M')) when 'today' scope = scope.between_day(DateTime.now) end @events = scope.all end
  66. def index(params) scope = Event case params[:timeframe] when 'tomorrow' scope = scope.between_day(DateTime.now + 1) when 'this_week' scope = scope.between_dates(DateTime.now, ( DateTime.now + 6 ).end_of_day) when 'custom' if params[:start_date].blank? params[:start_date] = DateTime.now.beginning_of_week.strftime('%m/%d/%Y') end if params[:end_date].blank? params[:end_date] = (DateTime.now.end_of_week - 2).strftime('%m/%d/%Y') end scope = scope.between_dates(DateTime.strptime(params[:start_date], '%m/%d/%Y'), DateTime.strptime(params[:end_date], '%m/%d/%Y').end_of_day) when 'hour' scope = scope.between_hour_on_day(DateTime.strptime(params[:hour], '%m/%d/%Y %H: %M')) when 'today' scope = scope.between_day(DateTime.now) end multiple named scopes @events = scope.all end
  67. class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => ["at >= ? AND at <= ?", start_date, end_date ] } } named_scope :between_hour_on_day, lambda { |start_hour| end_date = start_hour + 1.hour-1.second { :conditions => {:at => start_hour..end_date} } } named_scope :between_day, lambda { |date| start_date = date.at_beginning_of_day end_date = start_date.end_of_day { :conditions => {:at => start_date..end_date} } } end
  68. different ways to do the same class Event < ActiveRecord::Base thing named_scope :between_dates, lambda { |start_date, end_date| {:conditions => ["at >= ? AND at <= ?", start_date, end_date ] } } named_scope :between_hour_on_day, lambda { |start_hour| end_date = start_hour + 1.hour-1.second { :conditions => {:at => start_hour..end_date} } } named_scope :between_day, lambda { |date| start_date = date.at_beginning_of_day end_date = start_date.end_of_day { :conditions => {:at => start_date..end_date} } } end
  69. class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => ["at >= ? AND at <= ?", start_date, end_date ] } } named_scope :between_hour_on_day, lambda { |start_hour| end_date = start_hour + 1.hour-1.second { :conditions => {:at => start_hour..end_date} } } more logic named_scope :between_day, lambda { |date| start_date = date.at_beginning_of_day end_date = start_date.end_of_day { :conditions => {:at => start_date..end_date} } } end
  70. Objectives Active: • Isolate unit tests from the database Passive: • Deal with bad smells
  71. Strategizing 1. Centralize date manipulation logic 2. Unify named scopes 3. Extract logic from Controller 4. Break timeframe into separate pieces
  72. def index(params) case params[:timeframe] when 'tomorrow' date = DateTime.now + 1 start_date = date.at_beginning_of_day end_date = start_date.end_of_day when 'this_week' start_date = DateTime.now end_date = (DateTime.now + 6 ).end_of_day when 'custom' start_date = params[:start_date].blank? ? DateTime.now.beginning_of_week : DateTime.strptime(params[:start_date], '%m/%d/%Y') end_date = params[:end_date].blank? ? (DateTime.now.end_of_week - 2) : DateTime.strptime(params[:end_date], '%m/%d/%Y').end_of_day when 'hour' start_date = DateTime.strptime(params[:hour], '%m/%d/%Y %H:%M') end_date = start_date + 1.hour-1.second when 'today' date = DateTime.now start_date = date.at_beginning_of_day end_date = start_date.end_of_day end @events = Event.between_dates(start_date, end_date) end
  73. class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => {:at => start_date..end_date}} } end
  74. class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => {:at => start_date..end_date}} } end lib/calendar_search_controller.rb | 27 +++++++++++++-------------- lib/event.rb | 13 +------------ spec/event_spec.rb | 37 ------------------------------------- 3 files changed, 14 insertions(+), 63 deletions(-)
  75. Before . ............ ds 0 .49367 secon Finished in res 13 examp les, 0 failu class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => {:at => start_date..end_date}} } end lib/calendar_search_controller.rb | 27 +++++++++++++-------------- lib/event.rb | 13 +------------ spec/event_spec.rb | 37 ------------------------------------- 3 files changed, 14 insertions(+), 63 deletions(-)
  76. Before ....... After . ............ ds Finished in 0 .49367 secon 0.25355 seco Finished in res 7 examples, nds 13 examp les, 0 failu 0 failures class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => {:at => start_date..end_date}} } end lib/calendar_search_controller.rb | 27 +++++++++++++-------------- lib/event.rb | 13 +------------ spec/event_spec.rb | 37 ------------------------------------- 3 files changed, 14 insertions(+), 63 deletions(-)
  77. Strategizing 1. Centralize date manipulation logic 2. Unify named scopes 3. Extract logic from Controller 4. Break timeframe into separate pieces
  78. class CalendarSearchController attr_reader :events def index(params) timeframe = TimeFrame.for(params[:timeframe], params[:start_date], params[:end_date], params[:hour]) @events = Event.between_dates(timeframe.start_date, timeframe.end_date) end end change to tests: it "should create time frame from params and retrieve events" do timeframe = TimeFrame.new(DateTime.now, DateTime.now) TimeFrame.should_receive(:for).with('today', 'start', 'end', 'hour').and_return(timeframe) events = [Event.new, Event.new] Event.should_receive(:between_dates).with(timeframe.start_date, timeframe.end_date).and_return(events) @controller.index(:timeframe => 'today', :start_date => 'start', :end_date => 'end', :hour => 'hour') @controller.events.should == events end
  79. describe "Acceptance tests" do around(:each) do |example| Event.transaction { example.run; raise ActiveRecord::Rollback } end before do @controller = CalendarSearchController.new @today = DateTime.strptime('06/01/2011', '%m/%d/%Y') DateTime.stub!(:now).and_return(@today) end it "should return today's events" do today_events = [Event.create(:at => @today), Event.create(:at => @today)] tomorrow = @today + 1 tomorrow_events = [Event.create(:at => tomorrow), Event.create(:at => tomorrow)] @controller.index(:timeframe => 'today') @controller.events.should == today_events end it "should return tomorrow's events" ... it "should return this week's events" ... it "should return events for specified date range" ... it "should default date range to this business week" ... it "returns events for a specified hour" ... end
  80. describe "Acceptance tests" do around(:each) do |example| After ...... Event.transaction { example.run; raise ActiveRecord::Rollback } end Finished in 0.24538 seco before do 6 examples, nds 0 failures @controller = CalendarSearchController.new @today = DateTime.strptime('06/01/2011', '%m/%d/%Y') DateTime.stub!(:now).and_return(@today) end it "should return today's events" do today_events = [Event.create(:at => @today), Event.create(:at => @today)] tomorrow = @today + 1 tomorrow_events = [Event.create(:at => tomorrow), Event.create(:at => tomorrow)] @controller.index(:timeframe => 'today') @controller.events.should == today_events end it "should return tomorrow's events" ... it "should return this week's events" ... it "should return events for specified date range" ... it "should default date range to this business week" ... it "returns events for a specified hour" ... end
  81. class TimeFrame < Struct.new(:start_date, :end_date) def self.for(type, start_date, end_date, hour) case type when 'tomorrow' date = DateTime.now + 1 TimeFrame.new(date.at_beginning_of_day, date.end_of_day) when 'this_week' TimeFrame.new(DateTime.now, (DateTime.now + 6 ).end_of_day) when 'custom' start_date = start_date.blank? ? DateTime.now.beginning_of_week : DateTime.strptime(start_date, '%m/%d/%Y') end_date = end_date.blank? ? (DateTime.now.end_of_week - 2) : DateTime.strptime(end_date, '%m/%d/%Y').end_of_day TimeFrame.new(start_date, end_date) when 'hour' start_date = DateTime.strptime(hour, '%m/%d/%Y %H:%M') end_date = start_date + 1.hour-1.second TimeFrame.new(start_date, end_date) when 'today' date = DateTime.now TimeFrame.new(date.at_beginning_of_day, date.end_of_day) end end end
  82. describe TimeFrame do before do @today = DateTime.strptime('06/01/2011', '%m/%d/%Y') DateTime.stub!(:now).and_return(@today) end describe "today's event" do it "should use beginning and end of day" do timeframe = TimeFrame.for('today', nil, nil, nil) timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end end ... end
  83. describe TimeFrame do before do @today = DateTime.strptime('06/01/2011', '%m/%d/%Y') DateTime.stub!(:now).and_return(@today) end describe "today's event" do it "should use beginning and end of day" do timeframe = TimeFrame.for('today', nil, nil, nil) timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end Rakefile | 4 ++ end lib/calendar_search_controller.rb | 24 +--------- ... lib/time_frame.rb | 24 ++++++++++ end spec/acceptance_tests.rb | 75 +++++++++++++++++++++++++++++++ spec/calendar_search_controller_spec.rb | 75 ++++--------------------------- spec/time_frame_spec.rb | 63 ++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 87 deletions(-)
  84. Before ....... ds describe d in 0 .25355 secon Finishe TimeFrame do es before les, 0 failur 7 examp do @today = DateTime.strptime('06/01/2011', '%m/%d/%Y') DateTime.stub!(:now).and_return(@today) end describe "today's event" do it "should use beginning and end of day" do timeframe = TimeFrame.for('today', nil, nil, nil) timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end Rakefile | 4 ++ end lib/calendar_search_controller.rb | 24 +--------- ... lib/time_frame.rb | 24 ++++++++++ end spec/acceptance_tests.rb | 75 +++++++++++++++++++++++++++++++ spec/calendar_search_controller_spec.rb | 75 ++++--------------------------- spec/time_frame_spec.rb | 63 ++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 87 deletions(-)
  85. Before ........ After ....... ds Finished in describe d in 0 .25355 secon she TimeFrame do 0.17938 seco nds Fini 8 examples, before les, 0 failures 7 ex amp do 0 failures @today = DateTime.strptime('06/01/2011', '%m/%d/%Y') DateTime.stub!(:now).and_return(@today) end describe "today's event" do it "should use beginning and end of day" do timeframe = TimeFrame.for('today', nil, nil, nil) timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end Rakefile | 4 ++ end lib/calendar_search_controller.rb | 24 +--------- ... lib/time_frame.rb | 24 ++++++++++ end spec/acceptance_tests.rb | 75 +++++++++++++++++++++++++++++++ spec/calendar_search_controller_spec.rb | 75 ++++--------------------------- spec/time_frame_spec.rb | 63 ++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 87 deletions(-)
  86. Strategizing 1. Centralize date manipulation logic 2. Unify named scopes 3. Extract logic from Controller 4. Break timeframe into separate pieces
  87. class TimeFrame attr_reader :start_date, :end_date class Today < TimeFrame def initialize date = DateTime.now @start_date, @end_date = date.at_beginning_of_day, date.end_of_day end end ... end describe TimeFrame::Today do it "should use beginning and end of day" do timeframe = TimeFrame::Today.new timeframe.start_date.should == @today.at_beginning_of_day timeframe.end_date.should == @today.end_of_day end ... end
  88. class TimeFrame ... def self.for(type, start_date, end_date, hour) case type when 'tomorrow' then Tomorrow.new when 'this_week' then ThisWeek.new when 'custom' then Custom.new(start_date, end_date) when 'hour' then Hour.new(hour) when 'today' then Today.new end end end describe TimeFrame::Today do ... describe "parsing" do it "should parse today's timeframe" do TimeFrame.for('today', nil, nil, nil).should be_an_instance_of(TimeFrame::Today) end ... end end
  89. class TimeFrame ... Before .def .self.for(type, start_date, end_date, hour) ... ... case type ds 0 .17938 secon when 'tomorrow' then Tomorrow.new Finished in when les, 0 failures ThisWeek.new 8 examp 'this_week' then when 'custom' then Custom.new(start_date, end_date) when 'hour' then Hour.new(hour) when 'today' then Today.new end end end describe TimeFrame::Today do ... describe "parsing" do it "should parse today's timeframe" do TimeFrame.for('today', nil, nil, nil).should be_an_instance_of(TimeFrame::Today) end ... end end
  90. class TimeFrame ... Before ............ After def .self.for(type, start_date, end_date, hour) . .... ... case type ds Finished in when d in 0 .17938 secon she 'tomorrow' then Tomorrow.new 0.19023 seco nds Fini 13 examples, when les, 0 failures ThisWeek.new 8 examp 'this_week' then 0 failures when 'custom' then Custom.new(start_date, end_date) when 'hour' then Hour.new(hour) when 'today' then Today.new end end end describe TimeFrame::Today do ... describe "parsing" do it "should parse today's timeframe" do TimeFrame.for('today', nil, nil, nil).should be_an_instance_of(TimeFrame::Today) end ... end end
  91. class TimeFrame ... Before ............ After def .self.for(type, start_date, end_date, hour) . .... ... case type ds Finished in when d in 0 .17938 secon she 'tomorrow' then Tomorrow.new 0.19023 seco nds Fini 13 examples, when les, 0 failures ThisWeek.new 8 examp 'this_week' then 0 failures when 'custom' then Custom.new(start_date, end_date) when 'hour' then Hour.new(hour) when 'today' then Today.new end end end describe TimeFrame::Today do ... describe "parsing" do lib/time_frame.rb | 59 +++++++++++++++++++++++++++++++++------------- it "should parse today's timeframe" do spec/time_frame_spec.rb | 44 ++++++++++++++++++++++++++-------- TimeFrame.for('today', nil, nil, nil).should 2 files changed, 75 insertions(+), 28 deletions(-) be_an_instance_of(TimeFrame::Today) end ... end end
  92. “Big Picture” • Architectural Changes • Consolidating different approaches • Substituting components/frameworks
  93. “Big Picture”
  94. “Big Picture” Scope Risk Effort Communication
  95. Change
  96. x x x x x x x Change x xx x
  97. x x x x x x x Change x xx x
  98. x x x x x x x x x xx x Change xxx x xx x
  99. x x xx x x x x x x x x x x xx x Change xxx x xx x
  100. x x xx x x x x x x x x x x xx x Change xxx x xx x xx xx x x xxx xx x
  101. x x x x x x x x xx x x x x x x x x x x x x x xx x x Change xxx x x x xx x x x x xx xx x x x x x xxx xx x x x x x
  102. Change
  103. x x x x x x x Change x xx x
  104. x x x x x x x Change x xx x
  105. x Prereq x x x x x x Change x xx x
  106. x Prereq x x x Prereq x x x Change x xx x
  107. x Prereq x x x Prereq x x x Change x xx Prereq x
  108. Prereq Prereq Change Prereq
  109. x x Prereq Prereq xx x Change xxx Prereq
  110. x Prereq x Prereq Prereq xx x Prereq Change xxx Prereq
  111. Prereq Prereq Prereq Prereq Change Prereq
  112. Prereq Prereq Prereq Prereq Change Prereq
  113. Prereq Prereq Prereq Prereq ✔ Change Prereq
  114. Prereq Prereq Prereq Prereq ✔ Change Prereq
  115. Prereq ✔ Prereq Prereq Prereq ✔ Change Prereq
  116. Prereq ✔ Prereq ✔ Prereq Prereq ✔ Change Prereq
  117. Prereq ✔ Prereq ✔ Prereq Prereq ✔ Change Prereq
  118. Prereq ✔ Prereq ✔ Prereq ✔ Prereq ✔ Change Prereq
  119. Prereq ✔ Prereq ✔ Prereq ✔ Prereq ✔ Change Prereq
  120. Prereq ✔ Prereq ✔ Prereq ✔ Prereq ✔ Change Prereq ✔
  121. Prereq ✔ Prereq ✔ Prereq ✔ Prereq ✔ Change ✔ Prereq ✔
  122. A story APP
  123. A story APP SVC
  124. A story APP SVC
  125. A story APP SVC
  126. The truth APP Model View Controller
  127. The truth APP Model View Controller Helper
  128. The truth APP Model View SVC Controller Helper
  129. The truth APP Model View SVC Controller everything is going to break !!! Helper
  130. Transitional Architecture APP Model View Controller SVC Helper
  131. Transitional Architecture APP Model View Controller SVC Helper
  132. Transitional Architecture APP Model View Controller SVC Helper
  133. Transitional Architecture APP Model View Controller SVC Helper
  134. Transitional Architecture APP Model View Controller SVC Helper
  135. Transitional Architecture APP Model View Controller SVC Helper
  136. Transitional Architecture • Go: • “Branch by Abstraction” • Migrating from iBatis to Hibernate • Migrating from Velocity/JsTemplate to JRuby on Rails
  137. Transitional Architecture • Go: • “Branch by Abstraction” • Migrating from iBatis to Hibernate • Migrating from Velocity/JsTemplate to JRuby on Rails - 0 11/05/make-large http://continuo usdelivery.com/2 - l y-with-branch-by scale-cha nges-incremental abstraction/
  138. References Books: "Refactoring: Improving the Design of Existing Code" - Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts "Refactoring to Patterns" - Joshua Kerievsky "Working Effectively with Legacy Code" - Michael Feathers "Beheading the Software Beast: Relentless Restructurings with The Mikado Method" - Daniel Brolund, Ola Ellnestam Credits (fotos): http://www.flickr.com/photos/dhammza/2227347832/ http://www.flickr.com/photos/pragdave/173640462/ Links: http://paulhammant.com/2011/09/01/refactoring-experiment http://continuousdelivery.com/2011/05/make-large-scale-changes-incrementally-with- branch-by-abstraction/ Code: https://github.com/dtsato/refactoring_experiment https://github.com/dtsato/kata-refactoring-calendar
  139. Obrigado! Danilo Sato - @dtsato ThoughtWorks www.dtsato.com

Editor's Notes

  • \n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  • Assumptions:\n- You&amp;#x2019;re not in a complete mess\n- You have tests\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • zoom out\n
  • zoom out\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Mec&amp;#xE2;nica: IDE resolve, ferramentas, cat&amp;#xE1;logos...\n
  • Exerc&amp;#xED;cios para praticar\nMelhoram uma habilidade espec&amp;#xED;fica: TDD, Algoritmos, Design, C&amp;#xF3;digo ruim? etc...\n
  • Cobertura 100%\nApplica&amp;#xE7;&amp;#xE3;o Java/Spring\n
  • \n
  • Objetivo: extrair placeOrder para outro controller\n
  • Extracting placeOrder() into a new OrderController class, and a new OrderControllerTest.\nNote: Controller Annotation is copy/pasted\n
  • Note: Controller name first typo (Products -&gt; SuggestedProducts)\n\n
  • Extracting Upseller collaborator\nNote: Controller annotation needs to be copy/pasted\n
  • \n
  • N&amp;#xE3;o tem segredo para identificar objetivo (NFRs)\n
  • N&amp;#xE3;o tem segredo para identificar objetivo (NFRs)\n
  • N&amp;#xE3;o tem segredo para identificar objetivo (NFRs)\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  • Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  • Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  • Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  • Aplica&amp;#xE7;&amp;#xE3;o Rails. Cobertura 100%\nForm de busca de eventos\nEm Ruby -&gt; n&amp;#xE3;o tem mesmas ferramentas avan&amp;#xE7;adas de refatora&amp;#xE7;&amp;#xE3;o\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • * Escopo &amp;#xFA;nico: between_dates\n* Mais feio (indo para tr&amp;#xE1;s antes de ir para frente)\n* Isolando mudan&amp;#xE7;a (toda a l&amp;#xF3;gica de datas num &amp;#xFA;nico lugar)\n
  • * Remo&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo &gt; Adi&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo\n* Testes mais r&amp;#xE1;pidos pois os testes dos 3 named scopes acessavam o BD\n
  • * Remo&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo &gt; Adi&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo\n* Testes mais r&amp;#xE1;pidos pois os testes dos 3 named scopes acessavam o BD\n
  • * Remo&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo &gt; Adi&amp;#xE7;&amp;#xE3;o de c&amp;#xF3;digo\n* Testes mais r&amp;#xE1;pidos pois os testes dos 3 named scopes acessavam o BD\n
  • \n
  • Skinny Controller\nTestes do Controller usam mocks para testar colabora&amp;#xE7;&amp;#xE3;o\n
  • Testes do controller moveram para teste de aceita&amp;#xE7;&amp;#xE3;o (cucumber?)\n\n
  • * Timeframe s&amp;#xF3; cont&amp;#xEA;m start/end\n* L&amp;#xF3;gica ainda &amp;#xE9; feia\n
  • * Teste n&amp;#xE3;o acessa mais o BD!\n* Adi&amp;#xE7;&amp;#xE3;o de muitos testes\n* Pouca mudan&amp;#xE7;a no c&amp;#xF3;digo em si\n
  • * Teste n&amp;#xE3;o acessa mais o BD!\n* Adi&amp;#xE7;&amp;#xE3;o de muitos testes\n* Pouca mudan&amp;#xE7;a no c&amp;#xF3;digo em si\n
  • * Teste n&amp;#xE3;o acessa mais o BD!\n* Adi&amp;#xE7;&amp;#xE3;o de muitos testes\n* Pouca mudan&amp;#xE7;a no c&amp;#xF3;digo em si\n
  • \n
  • * Separa&amp;#xE7;&amp;#xE3;o de subclasses em TimeFrame para remover switch/case\n* Testes mudam junto\n
  • * Factory method ainda tem switch/case (discutir poss&amp;#xED;vel uso de metaprograma&amp;#xE7;&amp;#xE3;o)\n* Redu&amp;#xE7;&amp;#xE3;o total dos testes unit&amp;#xE1;rios: de 0.4936 para 0.19023 (61%)\n
  • * Factory method ainda tem switch/case (discutir poss&amp;#xED;vel uso de metaprograma&amp;#xE7;&amp;#xE3;o)\n* Redu&amp;#xE7;&amp;#xE3;o total dos testes unit&amp;#xE1;rios: de 0.4936 para 0.19023 (61%)\n
  • * Factory method ainda tem switch/case (discutir poss&amp;#xED;vel uso de metaprograma&amp;#xE7;&amp;#xE3;o)\n* Redu&amp;#xE7;&amp;#xE3;o total dos testes unit&amp;#xE1;rios: de 0.4936 para 0.19023 (61%)\n
  • \n
  • L&amp;#xED;der T&amp;#xE9;cnico: compartilha a vis&amp;#xE3;o\nWorkshops de arquitetura presente vs. futura\nRequires a disciplined approach to tackling large changes\n
  • L&amp;#xED;der T&amp;#xE9;cnico: compartilha a vis&amp;#xE3;o\nWorkshops de arquitetura presente vs. futura\nRequires a disciplined approach to tackling large changes\n
  • L&amp;#xED;der T&amp;#xE9;cnico: compartilha a vis&amp;#xE3;o\nWorkshops de arquitetura presente vs. futura\nRequires a disciplined approach to tackling large changes\n
  • L&amp;#xED;der T&amp;#xE9;cnico: compartilha a vis&amp;#xE3;o\nWorkshops de arquitetura presente vs. futura\nRequires a disciplined approach to tackling large changes\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Escolha objetivo, comece de forma inocente, aprenda e desenho um grafo, rollback quando est&amp;#xE1; quebrado, trabalhe das folhas para a ra&amp;#xED;z\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • Exemplo\n
  • \n
  • \n
  • \n

×