SlideShare a Scribd company logo
1 of 148
Refactoring at Large
     Danilo Sato - @dtsato
        ThoughtWorks
       www.dtsato.com
My Plan for Today
+ •    “Inside out” approach
  • Show me Code (Java)
  • Strategies and Objectives
  • Refactoring Ruby/Rails
- • Larger refactorings
Why do we change
     code?
Why do we change
       code?
• Add features
Why do we change
       code?
• Add features
• Fix bugs
Why do we change
       code?
• Add features
• Fix bugs
• Improve design
Why do we change
       code?
• Add features
• Fix bugs
• Improve design
• Optimization
Why do we change
       code?
• Add features
• Fix bugs
• Improve design
• Optimization
"Refactoring is a disciplined
 technique for restructuring an
existing body of code, altering its
    internal structure without
changing its external behavior."
Refactoring

    Code
Refactoring

    Code
Refactoring

    Code




           Extr act Method
Refactoring

    Code
Refactoring

    Code
Refactoring

    Code




           Eliminate Duplication
•   Extract Method

•   Move Method

•   Extract Class

•   Remove Middle Man

•   Self Encapsulate Field

•   Decompose Conditional

•   ...
Tests!
                Code

Test
Tests!
                Code

Test
Tests!
                     Code

Test




                Eliminate Duplication
Other types...

                    Code
Test
Other types...

                 Code   Code
Test
Other types...

                 Code   Code
Test   Test
Other types...

                 Code         Code
Test   Test




                   Split Responsibility
Other types...

                    Code
Test
Other types...

Test               Code
Other types...

Test               Code
Other types...

Test               Code




                   Change Design
Even more...
“Big Picture”
“Big Picture”
“Big Picture”
“Big Picture”
“Big Picture”
Strategy




Mechanics
Coding Kata!
Coding Kata!
CheckoutControllerTest   CheckoutController



                         Order        Cart
-
                                    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
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]]}}}"));
    }

}
@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"; }
}
Mechanics


                       t his video on:        riment
               Watch           factoring-expe
               com/dt sato/re
    p://vimeo.
htt
First Attempt...


                       t his video on:        riment
               Watch           factoring-expe
               com/dt sato/re
    p://vimeo.
htt
Strategy


                       t his video on:        riment
               Watch           factoring-expe
               com/dt sato/re
    p://vimeo.
htt
Identify your Objective
Identify your Objective
Identify your Objective
      “Active” design
Identify your Objective
            “Active” design




•   OO Design
Identify your Objective
              “Active” design




•   OO Design

•   Project direction
Identify your Objective
              “Active” design




•   OO Design

•   Project direction

•   Architectural decisions
Identify your Objective
      “Passive” design
Identify your Objective
                 “Passive” design




•   Bad Smells
Identify your Objective
                 “Passive” design




•   Bad Smells

•   Metrics
Identify your Objective
                 “Passive” design




•   Bad Smells

•   Metrics

•   Visualizations
Strategize
Strategize


    ?                ?

?
Strategize
Strategize

• Isolate the impact of Change
Strategize

• Isolate the impact of Change
• Baby Steps
Strategize

• Isolate the impact of Change
• Baby Steps
• Keep the tests green
Strategize

•   Isolate the impact of Change
•   Baby Steps
•   Keep the tests green
•   Moving backwards before moving forward
CalendarSearchController
                           CalendarSearchController
          Test



         Event                      Event
r
                       dtsato/kata-ref actoring-calenda
http ://github.com/




  CalendarSearchController
                                    CalendarSearchController
            Test



           Event                             Event
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
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
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
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
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
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
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
Objectives

Active:

 • Isolate unit tests from the database
Passive:

 • Deal with bad smells
Strategizing

1. Centralize date manipulation logic

2. Unify named scopes

3. Extract logic from Controller

4. Break timeframe into separate pieces
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
class Event < ActiveRecord::Base
  named_scope :between_dates, lambda { |start_date, end_date|
    {:conditions => {:at => start_date..end_date}}
  }
end
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(-)
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(-)
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(-)
Strategizing

1. Centralize date manipulation logic

2. Unify named scopes

3. Extract logic from Controller

4. Break timeframe into separate pieces
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
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
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
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
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
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(-)
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(-)
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(-)
Strategizing

1. Centralize date manipulation logic

2. Unify named scopes

3. Extract logic from Controller

4. Break timeframe into separate pieces
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
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
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
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
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
“Big Picture”

• Architectural Changes
• Consolidating different approaches
• Substituting components/frameworks
“Big Picture”
“Big Picture”



Scope    Risk   Effort   Communication
Change
x x            x x
  x              x x

      Change



      x xx
       x
x x            x x
  x              x x

      Change



      x xx
       x
x
         x

       x x                x x
         x                  x x
  xx
 x               Change
xxx

                 x xx
                  x
x                 x
                              xx x
         x                      x
       x x                x x
         x                  x x
  xx
 x               Change
xxx

                 x xx
                  x
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
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
Change
x x            x x
  x              x x

      Change



      x xx
       x
x x            x x
  x              x x

      Change



      x xx
       x
x
Prereq
       x            x x
       x              x x

           Change



           x xx
            x
x
Prereq
       x              x
                    x Prereq
       x              x x

           Change



           x xx
            x
x
Prereq
       x              x
                    x Prereq
       x              x x

           Change



           x xx
           Prereq
            x
Prereq            Prereq


         Change



         Prereq
x
                x

       Prereq                    Prereq

  xx
 x                      Change
xxx

                        Prereq
x
                         Prereq
                     x

            Prereq                    Prereq

  xx
 x Prereq                    Change
xxx

                             Prereq
Prereq


         Prereq              Prereq



Prereq              Change



                    Prereq
Prereq


         Prereq              Prereq



Prereq              Change



                    Prereq
Prereq


       Prereq              Prereq



Prereq ✔          Change



                  Prereq
Prereq


       Prereq              Prereq



Prereq ✔          Change



                  Prereq
Prereq   ✔
       Prereq                Prereq



Prereq ✔          Change



                  Prereq
Prereq   ✔
       Prereq   ✔            Prereq



Prereq ✔            Change



                    Prereq
Prereq   ✔
       Prereq   ✔            Prereq



Prereq ✔            Change



                    Prereq
Prereq   ✔
       Prereq   ✔            Prereq   ✔
Prereq ✔            Change



                    Prereq
Prereq   ✔
       Prereq   ✔            Prereq   ✔
Prereq ✔            Change



                    Prereq
Prereq   ✔
       Prereq   ✔                Prereq   ✔
Prereq ✔            Change



                    Prereq   ✔
Prereq   ✔
       Prereq   ✔                Prereq   ✔
Prereq ✔            Change   ✔

                    Prereq   ✔
A story


          APP
A story


                APP

SVC
A story


                APP

SVC
A story


                APP

SVC
The truth

              APP

              Model


              View


            Controller
The truth

              APP

              Model


              View


            Controller


             Helper
The truth

                APP

                Model


                View
SVC
              Controller


               Helper
The truth

                             APP

                             Model


                             View
SVC
                           Controller

        everything is
      going to break !!!
                            Helper
Transitional
Architecture
            APP

            Model


            View


          Controller

SVC
           Helper
Transitional
Architecture
            APP

            Model


            View


          Controller

SVC
           Helper
Transitional
Architecture
            APP

            Model


            View


          Controller

SVC
           Helper
Transitional
Architecture
            APP

            Model


            View


          Controller

SVC
           Helper
Transitional
Architecture
            APP

            Model


            View


          Controller

SVC
           Helper
Transitional
Architecture
            APP

            Model


            View


          Controller

SVC
           Helper
Transitional
         Architecture
• Go:
 • “Branch by Abstraction”
 • Migrating from iBatis to Hibernate
 • Migrating from Velocity/JsTemplate to
    JRuby on Rails
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/
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
Obrigado!
Danilo Sato - @dtsato
   ThoughtWorks
  www.dtsato.com

More Related Content

Similar to Refactoring code for improved design and readability

How I Learned to Stop Worrying and Love Legacy Code - Ox:Agile 2018
How I Learned to Stop Worrying and Love Legacy Code - Ox:Agile 2018How I Learned to Stop Worrying and Love Legacy Code - Ox:Agile 2018
How I Learned to Stop Worrying and Love Legacy Code - Ox:Agile 2018Mike Harris
 
3 things you must know to think reactive - Geecon Kraków 2015
3 things you must know to think reactive - Geecon Kraków 20153 things you must know to think reactive - Geecon Kraków 2015
3 things you must know to think reactive - Geecon Kraków 2015Manuel Bernhardt
 
Being Expressive in Code
Being Expressive in CodeBeing Expressive in Code
Being Expressive in CodeEamonn Boyle
 
Refatoração em Larga Escala
Refatoração em Larga EscalaRefatoração em Larga Escala
Refatoração em Larga EscalaDanilo Sato
 
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...JavaScripters Community
 
GDG Addis - An Introduction to Django and App Engine
GDG Addis - An Introduction to Django and App EngineGDG Addis - An Introduction to Django and App Engine
GDG Addis - An Introduction to Django and App EngineYared Ayalew
 
Boost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineeringBoost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineeringMiro Wengner
 
Django’s nasal passage
Django’s nasal passageDjango’s nasal passage
Django’s nasal passageErik Rose
 
Hybrid rule engines (rulesfest 2010)
Hybrid rule engines (rulesfest 2010)Hybrid rule engines (rulesfest 2010)
Hybrid rule engines (rulesfest 2010)Geoffrey De Smet
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your CodeNate Abele
 
Measuring Your Code 2.0
Measuring Your Code 2.0Measuring Your Code 2.0
Measuring Your Code 2.0Nate Abele
 
Surviving javascript.pptx
Surviving javascript.pptxSurviving javascript.pptx
Surviving javascript.pptxTamas Rev
 
The Art Of Readable Code
The Art Of Readable CodeThe Art Of Readable Code
The Art Of Readable CodeBaidu, Inc.
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Michelangelo van Dam
 
Agile experiments in Machine Learning with F#
Agile experiments in Machine Learning with F#Agile experiments in Machine Learning with F#
Agile experiments in Machine Learning with F#J On The Beach
 
Advance sql - window functions patterns and tricks
Advance sql - window functions patterns and tricksAdvance sql - window functions patterns and tricks
Advance sql - window functions patterns and tricksEyal Trabelsi
 

Similar to Refactoring code for improved design and readability (20)

How I Learned to Stop Worrying and Love Legacy Code - Ox:Agile 2018
How I Learned to Stop Worrying and Love Legacy Code - Ox:Agile 2018How I Learned to Stop Worrying and Love Legacy Code - Ox:Agile 2018
How I Learned to Stop Worrying and Love Legacy Code - Ox:Agile 2018
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
 
Test driven development
Test driven developmentTest driven development
Test driven development
 
3 things you must know to think reactive - Geecon Kraków 2015
3 things you must know to think reactive - Geecon Kraków 20153 things you must know to think reactive - Geecon Kraków 2015
3 things you must know to think reactive - Geecon Kraków 2015
 
Stop that!
Stop that!Stop that!
Stop that!
 
Being Expressive in Code
Being Expressive in CodeBeing Expressive in Code
Being Expressive in Code
 
Refatoração em Larga Escala
Refatoração em Larga EscalaRefatoração em Larga Escala
Refatoração em Larga Escala
 
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
JavaScripters Event Oct 22, 2016 · 2:00 PM: Common Mistakes made by Angular D...
 
GDG Addis - An Introduction to Django and App Engine
GDG Addis - An Introduction to Django and App EngineGDG Addis - An Introduction to Django and App Engine
GDG Addis - An Introduction to Django and App Engine
 
Qtp - Introduction to synchronization
Qtp -  Introduction to synchronizationQtp -  Introduction to synchronization
Qtp - Introduction to synchronization
 
Boost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineeringBoost delivery stream with code discipline engineering
Boost delivery stream with code discipline engineering
 
Django’s nasal passage
Django’s nasal passageDjango’s nasal passage
Django’s nasal passage
 
Hybrid rule engines (rulesfest 2010)
Hybrid rule engines (rulesfest 2010)Hybrid rule engines (rulesfest 2010)
Hybrid rule engines (rulesfest 2010)
 
Measuring Your Code
Measuring Your CodeMeasuring Your Code
Measuring Your Code
 
Measuring Your Code 2.0
Measuring Your Code 2.0Measuring Your Code 2.0
Measuring Your Code 2.0
 
Surviving javascript.pptx
Surviving javascript.pptxSurviving javascript.pptx
Surviving javascript.pptx
 
The Art Of Readable Code
The Art Of Readable CodeThe Art Of Readable Code
The Art Of Readable Code
 
Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12Workshop quality assurance for php projects tek12
Workshop quality assurance for php projects tek12
 
Agile experiments in Machine Learning with F#
Agile experiments in Machine Learning with F#Agile experiments in Machine Learning with F#
Agile experiments in Machine Learning with F#
 
Advance sql - window functions patterns and tricks
Advance sql - window functions patterns and tricksAdvance sql - window functions patterns and tricks
Advance sql - window functions patterns and tricks
 

More from Danilo Sato

Padrões de deploy para devops e entrega contínua - DevDay 2014
Padrões de deploy para devops e entrega contínua - DevDay 2014Padrões de deploy para devops e entrega contínua - DevDay 2014
Padrões de deploy para devops e entrega contínua - DevDay 2014Danilo Sato
 
Keynote RuPy Natal 2014
Keynote RuPy Natal 2014Keynote RuPy Natal 2014
Keynote RuPy Natal 2014Danilo Sato
 
Padrões de deploy para DevOps e Entrega Contínua
Padrões de deploy para DevOps e Entrega ContínuaPadrões de deploy para DevOps e Entrega Contínua
Padrões de deploy para DevOps e Entrega ContínuaDanilo Sato
 
Padrões de deploy para DevOps e Entrega Contínua
Padrões de deploy para DevOps e Entrega ContínuaPadrões de deploy para DevOps e Entrega Contínua
Padrões de deploy para DevOps e Entrega ContínuaDanilo Sato
 
Refactoring Strategies: Beyond the Basics
Refactoring Strategies: Beyond the BasicsRefactoring Strategies: Beyond the Basics
Refactoring Strategies: Beyond the BasicsDanilo Sato
 
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a ObjetosO que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a ObjetosDanilo Sato
 
Princípios e Práticas para lidar com requisitos não-funcionais em desenvolvim...
Princípios e Práticas para lidar com requisitos não-funcionais em desenvolvim...Princípios e Práticas para lidar com requisitos não-funcionais em desenvolvim...
Princípios e Práticas para lidar com requisitos não-funcionais em desenvolvim...Danilo Sato
 
Estratégias de Refatoração: além do be-a-bá
Estratégias de Refatoração: além do be-a-báEstratégias de Refatoração: além do be-a-bá
Estratégias de Refatoração: além do be-a-báDanilo Sato
 
Coding Dojo Introduction
Coding Dojo IntroductionCoding Dojo Introduction
Coding Dojo IntroductionDanilo Sato
 
Direto das Trincheiras: Boas e más práticas de aplicações Ruby em ambientes c...
Direto das Trincheiras: Boas e más práticas de aplicações Ruby em ambientes c...Direto das Trincheiras: Boas e más práticas de aplicações Ruby em ambientes c...
Direto das Trincheiras: Boas e más práticas de aplicações Ruby em ambientes c...Danilo Sato
 
Managing your technical debt - AgileBrazil 2011
Managing your technical debt - AgileBrazil 2011Managing your technical debt - AgileBrazil 2011
Managing your technical debt - AgileBrazil 2011Danilo Sato
 

More from Danilo Sato (12)

Padrões de deploy para devops e entrega contínua - DevDay 2014
Padrões de deploy para devops e entrega contínua - DevDay 2014Padrões de deploy para devops e entrega contínua - DevDay 2014
Padrões de deploy para devops e entrega contínua - DevDay 2014
 
Keynote RuPy Natal 2014
Keynote RuPy Natal 2014Keynote RuPy Natal 2014
Keynote RuPy Natal 2014
 
Padrões de deploy para DevOps e Entrega Contínua
Padrões de deploy para DevOps e Entrega ContínuaPadrões de deploy para DevOps e Entrega Contínua
Padrões de deploy para DevOps e Entrega Contínua
 
Padrões de deploy para DevOps e Entrega Contínua
Padrões de deploy para DevOps e Entrega ContínuaPadrões de deploy para DevOps e Entrega Contínua
Padrões de deploy para DevOps e Entrega Contínua
 
Refactoring Strategies: Beyond the Basics
Refactoring Strategies: Beyond the BasicsRefactoring Strategies: Beyond the Basics
Refactoring Strategies: Beyond the Basics
 
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a ObjetosO que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
 
Princípios e Práticas para lidar com requisitos não-funcionais em desenvolvim...
Princípios e Práticas para lidar com requisitos não-funcionais em desenvolvim...Princípios e Práticas para lidar com requisitos não-funcionais em desenvolvim...
Princípios e Práticas para lidar com requisitos não-funcionais em desenvolvim...
 
Estratégias de Refatoração: além do be-a-bá
Estratégias de Refatoração: além do be-a-báEstratégias de Refatoração: além do be-a-bá
Estratégias de Refatoração: além do be-a-bá
 
Coding Dojo Introduction
Coding Dojo IntroductionCoding Dojo Introduction
Coding Dojo Introduction
 
Direto das Trincheiras: Boas e más práticas de aplicações Ruby em ambientes c...
Direto das Trincheiras: Boas e más práticas de aplicações Ruby em ambientes c...Direto das Trincheiras: Boas e más práticas de aplicações Ruby em ambientes c...
Direto das Trincheiras: Boas e más práticas de aplicações Ruby em ambientes c...
 
Managing your technical debt - AgileBrazil 2011
Managing your technical debt - AgileBrazil 2011Managing your technical debt - AgileBrazil 2011
Managing your technical debt - AgileBrazil 2011
 
Lean Lego Game
Lean Lego GameLean Lego Game
Lean Lego Game
 

Recently uploaded

Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch TuesdayIvanti
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Scott Andery
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality AssuranceInflectra
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesThousandEyes
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 

Recently uploaded (20)

Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch Tuesday
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 

Refactoring code for improved design and readability

  • 1. Refactoring at Large Danilo Sato - @dtsato ThoughtWorks www.dtsato.com
  • 2.
  • 3.
  • 4.
  • 5.
  • 6. My Plan for Today + • “Inside out” approach • Show me Code (Java) • Strategies and Objectives • Refactoring Ruby/Rails - • Larger refactorings
  • 7.
  • 8. Why do we change code?
  • 9. Why do we change code? • Add features
  • 10. Why do we change code? • Add features • Fix bugs
  • 11. Why do we change code? • Add features • Fix bugs • Improve design
  • 12. Why do we change code? • Add features • Fix bugs • Improve design • Optimization
  • 13. Why do we change code? • Add features • Fix bugs • Improve design • Optimization
  • 14.
  • 15.
  • 16.
  • 17. "Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior."
  • 18. Refactoring Code
  • 19. Refactoring Code
  • 20. Refactoring Code Extr act Method
  • 21. Refactoring Code
  • 22. Refactoring Code
  • 23. Refactoring Code Eliminate Duplication
  • 24. Extract Method • Move Method • Extract Class • Remove Middle Man • Self Encapsulate Field • Decompose Conditional • ...
  • 25. Tests! Code Test
  • 26. Tests! Code Test
  • 27. Tests! Code Test Eliminate Duplication
  • 28. Other types... Code Test
  • 29. Other types... Code Code Test
  • 30. Other types... Code Code Test Test
  • 31. Other types... Code Code Test Test Split Responsibility
  • 32. Other types... Code Test
  • 35. Other types... Test Code Change Design
  • 45. CheckoutControllerTest CheckoutController Order Cart
  • 46. - 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
  • 47. 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]]}}}")); } }
  • 48. @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"; } }
  • 49. Mechanics t his video on: riment Watch factoring-expe com/dt sato/re p://vimeo. htt
  • 50. First Attempt... t his video on: riment Watch factoring-expe com/dt sato/re p://vimeo. htt
  • 51. Strategy t his video on: riment Watch factoring-expe com/dt sato/re p://vimeo. htt
  • 54. Identify your Objective “Active” design
  • 55. Identify your Objective “Active” design • OO Design
  • 56. Identify your Objective “Active” design • OO Design • Project direction
  • 57. Identify your Objective “Active” design • OO Design • Project direction • Architectural decisions
  • 58. Identify your Objective “Passive” design
  • 59. Identify your Objective “Passive” design • Bad Smells
  • 60. Identify your Objective “Passive” design • Bad Smells • Metrics
  • 61. Identify your Objective “Passive” design • Bad Smells • Metrics • Visualizations
  • 63. Strategize ? ? ?
  • 65. Strategize • Isolate the impact of Change
  • 66. Strategize • Isolate the impact of Change • Baby Steps
  • 67. Strategize • Isolate the impact of Change • Baby Steps • Keep the tests green
  • 68. Strategize • Isolate the impact of Change • Baby Steps • Keep the tests green • Moving backwards before moving forward
  • 69. CalendarSearchController CalendarSearchController Test Event Event
  • 70. r dtsato/kata-ref actoring-calenda http ://github.com/ CalendarSearchController CalendarSearchController Test Event Event
  • 71. 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
  • 72. 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
  • 73. 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
  • 74. 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
  • 75. 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
  • 76. 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
  • 77. 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
  • 78. Objectives Active: • Isolate unit tests from the database Passive: • Deal with bad smells
  • 79. Strategizing 1. Centralize date manipulation logic 2. Unify named scopes 3. Extract logic from Controller 4. Break timeframe into separate pieces
  • 80. 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
  • 81. class Event < ActiveRecord::Base named_scope :between_dates, lambda { |start_date, end_date| {:conditions => {:at => start_date..end_date}} } end
  • 82. 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(-)
  • 83. 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(-)
  • 84. 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(-)
  • 85. Strategizing 1. Centralize date manipulation logic 2. Unify named scopes 3. Extract logic from Controller 4. Break timeframe into separate pieces
  • 86. 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
  • 87. 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
  • 88. 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
  • 89. 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
  • 90. 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
  • 91. 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(-)
  • 92. 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(-)
  • 93. 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(-)
  • 94. Strategizing 1. Centralize date manipulation logic 2. Unify named scopes 3. Extract logic from Controller 4. Break timeframe into separate pieces
  • 95. 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
  • 96. 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
  • 97. 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
  • 98. 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
  • 99. 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
  • 100. “Big Picture” • Architectural Changes • Consolidating different approaches • Substituting components/frameworks
  • 102. “Big Picture” Scope Risk Effort Communication
  • 103. Change
  • 104. x x x x x x x Change x xx x
  • 105. x x x x x x x Change x xx x
  • 106. x x x x x x x x x xx x Change xxx x xx x
  • 107. x x xx x x x x x x x x x x xx x Change xxx x xx x
  • 108. 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
  • 109. 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
  • 110.
  • 111. Change
  • 112. x x x x x x x Change x xx x
  • 113. x x x x x x x Change x xx x
  • 114. x Prereq x x x x x x Change x xx x
  • 115. x Prereq x x x Prereq x x x Change x xx x
  • 116. x Prereq x x x Prereq x x x Change x xx Prereq x
  • 117. Prereq Prereq Change Prereq
  • 118. x x Prereq Prereq xx x Change xxx Prereq
  • 119. x Prereq x Prereq Prereq xx x Prereq Change xxx Prereq
  • 120. Prereq Prereq Prereq Prereq Change Prereq
  • 121. Prereq Prereq Prereq Prereq Change Prereq
  • 122. Prereq Prereq Prereq Prereq ✔ Change Prereq
  • 123. Prereq Prereq Prereq Prereq ✔ Change Prereq
  • 124. Prereq ✔ Prereq Prereq Prereq ✔ Change Prereq
  • 125. Prereq ✔ Prereq ✔ Prereq Prereq ✔ Change Prereq
  • 126. Prereq ✔ Prereq ✔ Prereq Prereq ✔ Change Prereq
  • 127. Prereq ✔ Prereq ✔ Prereq ✔ Prereq ✔ Change Prereq
  • 128. Prereq ✔ Prereq ✔ Prereq ✔ Prereq ✔ Change Prereq
  • 129. Prereq ✔ Prereq ✔ Prereq ✔ Prereq ✔ Change Prereq ✔
  • 130. Prereq ✔ Prereq ✔ Prereq ✔ Prereq ✔ Change ✔ Prereq ✔
  • 131. A story APP
  • 132. A story APP SVC
  • 133. A story APP SVC
  • 134. A story APP SVC
  • 135. The truth APP Model View Controller
  • 136. The truth APP Model View Controller Helper
  • 137. The truth APP Model View SVC Controller Helper
  • 138. The truth APP Model View SVC Controller everything is going to break !!! Helper
  • 139. Transitional Architecture APP Model View Controller SVC Helper
  • 140. Transitional Architecture APP Model View Controller SVC Helper
  • 141. Transitional Architecture APP Model View Controller SVC Helper
  • 142. Transitional Architecture APP Model View Controller SVC Helper
  • 143. Transitional Architecture APP Model View Controller SVC Helper
  • 144. Transitional Architecture APP Model View Controller SVC Helper
  • 145. Transitional Architecture • Go: • “Branch by Abstraction” • Migrating from iBatis to Hibernate • Migrating from Velocity/JsTemplate to JRuby on Rails
  • 146. 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/
  • 147. 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
  • 148. Obrigado! Danilo Sato - @dtsato ThoughtWorks www.dtsato.com

Editor's Notes

  1. \n
  2. Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  3. Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  4. Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  5. Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  6. Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  7. Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  8. Developer, Agile Coach, Tech Lead, DevOps, Trainer\n
  9. Assumptions:\n- You&amp;#x2019;re not in a complete mess\n- You have tests\n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. zoom out\n
  17. zoom out\n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. Mec&amp;#xE2;nica: IDE resolve, ferramentas, cat&amp;#xE1;logos...\n
  66. Exerc&amp;#xED;cios para praticar\nMelhoram uma habilidade espec&amp;#xED;fica: TDD, Algoritmos, Design, C&amp;#xF3;digo ruim? etc...\n
  67. Cobertura 100%\nApplica&amp;#xE7;&amp;#xE3;o Java/Spring\n
  68. \n
  69. Objetivo: extrair placeOrder para outro controller\n
  70. Extracting placeOrder() into a new OrderController class, and a new OrderControllerTest.\nNote: Controller Annotation is copy/pasted\n
  71. Note: Controller name first typo (Products -&gt; SuggestedProducts)\n\n
  72. Extracting Upseller collaborator\nNote: Controller annotation needs to be copy/pasted\n
  73. \n
  74. N&amp;#xE3;o tem segredo para identificar objetivo (NFRs)\n
  75. N&amp;#xE3;o tem segredo para identificar objetivo (NFRs)\n
  76. N&amp;#xE3;o tem segredo para identificar objetivo (NFRs)\n
  77. \n
  78. \n
  79. \n
  80. \n
  81. \n
  82. \n
  83. \n
  84. \n
  85. Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  86. Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  87. Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  88. Michael Feathers: Effect Sketches\nCluster de m&amp;#xE9;todos/atributos\nMova c&amp;#xF3;digo repetido para um mesmo lugar\n
  89. 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
  90. \n
  91. \n
  92. \n
  93. \n
  94. \n
  95. \n
  96. \n
  97. \n
  98. \n
  99. \n
  100. * 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
  101. * 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
  102. * 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
  103. * 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
  104. \n
  105. Skinny Controller\nTestes do Controller usam mocks para testar colabora&amp;#xE7;&amp;#xE3;o\n
  106. Testes do controller moveram para teste de aceita&amp;#xE7;&amp;#xE3;o (cucumber?)\n\n
  107. * Timeframe s&amp;#xF3; cont&amp;#xEA;m start/end\n* L&amp;#xF3;gica ainda &amp;#xE9; feia\n
  108. * 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
  109. * 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
  110. * 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
  111. \n
  112. * Separa&amp;#xE7;&amp;#xE3;o de subclasses em TimeFrame para remover switch/case\n* Testes mudam junto\n
  113. * 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
  114. * 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
  115. * 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
  116. \n
  117. 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
  118. 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
  119. 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
  120. 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
  121. \n
  122. \n
  123. \n
  124. \n
  125. \n
  126. \n
  127. \n
  128. \n
  129. \n
  130. \n
  131. \n
  132. \n
  133. 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
  134. \n
  135. \n
  136. \n
  137. \n
  138. \n
  139. \n
  140. \n
  141. \n
  142. \n
  143. \n
  144. \n
  145. \n
  146. \n
  147. \n
  148. \n
  149. \n
  150. \n
  151. \n
  152. \n
  153. \n
  154. \n
  155. \n
  156. Exemplo\n
  157. Exemplo\n
  158. Exemplo\n
  159. Exemplo\n
  160. Exemplo\n
  161. Exemplo\n
  162. Exemplo\n
  163. Exemplo\n
  164. Exemplo\n
  165. Exemplo\n
  166. Exemplo\n
  167. Exemplo\n
  168. Exemplo\n
  169. Exemplo\n
  170. \n
  171. \n
  172. \n