SlideShare a Scribd company logo
1 of 59
WORKING EFFECTIVELY WITH LEGACY RAILS
              Pat Maddox & BJ Clark
Why Care About Legacy Code?


Rails is almost 5 years old

Lots of $$ to be made

Everyone writes legacy code
What is Legacy Code?



Code written >3 months ago

Code written by someone else
What is Legacy Code?



Michael Feathers - “Code Without Tests”

Ward Cunningham - “Accumulated technical debt”

DHH - “Code you wrote when weren’t as good as you are now”
Code that is difficult to change
TECHNICAL DEBT
What is Technical Debt?

“During the planning or execution of a software project, decisions are
   made to defer necessary work, this is creating technical debt”
                  http://c2.com/cgi/wiki?TechnicalDebt


 Why is it bad?

 Not necessarily bad (good vs bad debt)
Examples of Technical Debt


Rails

MediaWiki

ActiveScaffold

Code you write
Recognizing Debt


Easy upfront gains

Difficult to extend long-term (does the tool support new
requirements)

No clear migration path
Managing Technical Debt
 “Remove cruft as you go. Build simplicity and clarity in from the
       beginning, and never relent in keeping them in. “
                           -Ron Jeffries




Big Design Up Front

Agile
TATFT & RATFT


Virtuous cycle - Symbiotic Relationship

Anti-pattern: Red. Green. Deploy.

Pattern: Red. Green. Refactor. Deploy.
TESTING TO MINIMIZE DEBT
Goals



Maintain existing value

Add new value
Start Testing Now


What level?

Unit vs Acceptance

Cast a wide net
Ok, lets test

Take 6 weeks off to write tests for your code
Ok, lets test

Take 6 weeks off to write tests for your code
Ok, lets test

Just in time

Characterization tests

  Cover existing functionality

  Never red, always green

Test-drive new features
Siloing Code



Webservice/SOA

Rails 3.0 mounted applications
REFACTORING TECHNIQUES
Rails already does it


Caching

has_many :through

Delegation, memoization, serialization

Validations and the .valid? api
Make everything a framework


All the benefits of abstracted code

Use existing frameworks

Write your own mini-frameworks
EXAMPLE
The Tests
                               it quot;works when updated by ownerquot; do
describe BoatsController do
 before(:each) do                do_update @owner
  @owner = User.create!          @boat.reload.name.should == quot;SS Minnowquot;
  @boat = Boat.create!         end
   :name => quot;Pequodquot;,
   :owner => @owner            it quot;works when updated by adminquot; do
 end                             admin = User.create! :admin => true
                                 do_update admin
 def do_update(user)             @boat.reload.name.should == quot;SS Minnowquot;
  login_as user                end
  put :update,
   :id => @boat.to_param,
                               it quot;fails when updated by non-ownerquot; do
   :boat =>
                                 nonowner = User.create!
     {:name => quot;SS Minnowquot;}
                                 do_update nonowner
 end
                                 @boat.reload.name.should == quot;Pequodquot;
                               end
The model


class Boat < ActiveRecord::Base
 belongs_to :owner, :class_name => quot;Userquot;

 def can_update?(user)
  user == owner || user.admin? || owner.blank?
 end
end
The controller
class BoatsController < ApplicationController
 def update
   @boat = Boat.find params[:id]
   if @boat.can_update?(current_user)
     if @boat.update_attributes(params[:boat])
       redirect_to boat_url(@boat)
     else
       render :action => quot;editquot;
     end
   else
     redirect_to boat_url(@boat)
   end
 end
end
WRITE A MINI-FRAMEWORK
The new controller
class BoatsController < ApplicationController
 require_authorization :update

 def update
  if @boat.update_attributes(params[:boat])
    redirect_to @boat
  else
    render :action => quot;editquot;
  end
 end

 def load_model
  @boat ||= Boat.find(params[:id])
 end
end
The “magic”
class ApplicationController < ActionController::Base
 def self.require_authorization(*actions)
   actions.each do |a|
    before_filter :check_authorization, :only => a
   end
 end

 def check_authorization
  model = load_model
  unless model.send(quot;can_#{action_name}?quot;, current_user)
    redirect_to model
  end
 end
end
What we’ve learned

Pros

  Easy to read

  Behavior for free

Cons

  Not as accessible to brand new Ruby programmers
USE EXISTING FRAMEWORKS
A little cleanup
class BoatsController < ApplicationController
 ...
 def object
   @boat ||= Boat.find(params[:id])
 end
end

class ApplicationController < ActionController::Base
  ...
 def check_authorization
    unless object.send(quot;can_#{action_name}?quot;, current_user)
      redirect_to model
    end
 end
end
resource_controller



class BoatsController < ApplicationController
 resource_controller # thanks James!!!
 require_authorization :update
end
Before
class BoatsController < ApplicationController
 def update
   @boat = Boat.find params[:id]
   if @boat.can_update?(current_user)
     if @boat.update_attributes(params[:boat])
       redirect_to boat_url(@boat)
     else
       render :action => quot;editquot;
     end
   else
     redirect_to boat_url(@boat)
   end
 end
end
After



class BoatsController < ApplicationController
 resource_controller # thanks James!!!
 require_authorization :update
end
METAPROGRAMMING ROCKS
METAPROGRAMMING ROCKS

   (with great power comes great responsibility)
LEVERAGE RAILS
Rails Extension Points


before_*/after_* hooks

AR observers

ActionController filters
The Test

describe AccountsController, quot;POST createquot; do
  def do_post
    post :create, :account => {:name => quot;Nikequot;}
  end

  it quot;should create a project for the accountquot; do
    do_post
    Account.first.should have(1).project
  end
end
The Controller

def create
  @account = Account.new params[:account]
  Account.transaction do
    if @account.save
      @account.projects.create :name => quot;#{@account.name}'s First Projectquot;
      redirect_to @account
    else
      render :template => quot;newquot;
    end
  end
end
Using Callbacks

class Account < ActiveRecord::Base
  has_many :projects

 after_create :create_project

  def create_project
    projects.create! :name => quot;#{name}'s First Projectquot;
  end
end
Simpler Controller

def create
  @account = Account.new params[:account]
  if @account.save
    redirect_to @account
  else
    render :template => quot;newquot;
  end
end
A little more
describe AccountsController, quot;POST createquot; do
  def do_post
    post :create, :account => {:name => quot;Nikequot;}
  end

 it quot;should create a project for the accountquot; do
   do_post
   Account.first.should have(1).project
 end

  it quot;should create a video for the projectquot; do
    do_post
    Account.first.projects.first.should have(1).video
  end
end
The Model
class Account < ActiveRecord::Base
  after_create :create_project
  ...
end

class Project < ActiveRecord::Base
  has_many :videos
  belongs_to :account

 after_create :create_video

  def create_video
    videos.create! :name => quot;#{account.name}'s First Videoquot;
  end
end
Tradeoffs


                               Cons
Pros

                                Indirection
  Free transaction semantics

                                Testing is a bit slower/
  Skinny controller, fat
                                tougher
  model
Modeling a Business Event
class AccountRegistration < ActiveRecord::Base
  belongs_to :account
  before_create :setup_account
  validates_associated :account
  attr_writer :name

  def setup_account
    self.account = Account.create :name => @name
    project = account.projects.create!
      :name => quot;#{@name}'s First Projectquot;
    project.videos.create! :name => quot;#{@name}'s First Videoquot;
  end
end
Controller & Other Models
# Use resource_controller
def create
  @registration = AccountRegistration.new params[:account]
  if @registration.save
    redirect_to @registration.account
  else
    render :template => quot;newquot;
  end
end

class Project < ActiveRecord::Base
  has_many :videos
end

class Account < ActiveRecord::Base
  has_many :projects
end
Modeling a Business Event
                                            class Account < ActiveRecord::Base
class AccountRegistration < AR::Base
                                              has_many :projects
  belongs_to :account
                                            end
  before_create :setup_account
  validates_associated :account
                                            class Project < ActiveRecord::Base
  attr_writer :name
                                              has_many :videos
                                            end
  def setup_account
    self.account = Account.create
      :name => @name
    project = account.projects.create!
      :name => quot;#{@name}'s First Projectquot;
    project.videos.create!
      :name => quot;#{@name}'s First Videoquot;
  end
end
SEAMS
Seams


Modify or sense behavior of code without changing it

OO - polymorphism

Dependency management
Ruby seams


alias_method_chain

method_missing

send / eval
class Order
 def initialize(us_state)
   @us_state = us_state
   @subtotal = 0
 end

 def add_item(item, quantity)
  @subtotal += (item.cost * quantity)
 end

 def tax
  TaxCalculator.calculate @subtotal, @us_state
 end
end

describe Order, quot;calculating taxquot; do
 it quot;should add tax onto the totalquot; do
   o = Order.new quot;CAquot;
   bacon = Item.new quot;Chunky baconquot;, 42
   o.add_item bacon, 1
   o.tax.should == 3.26
 end
end
DI to the rescue
class Order
 def tax(calculator)
   calculator.calculate @subtotal, @us_state
 end
end

describe Order, quot;calculating taxquot; do
 it quot;should add tax onto the totalquot; do
   fake_calculator = mock('calculator')
   fake_calculator.should_receive(:calculate).
     with(42, quot;CAquot;).and_return 3.26
   o = Order.new quot;CAquot;
   bacon = Item.new quot;Chunky baconquot;, 42
   o.add_item bacon, 1
   o.tax(fake_calculator).should == 3.26
 end
end
DI to the rescue
class Order
                                        Breaks   existing client code
 def tax(calculator)
   calculator.calculate @subtotal, @us_state
 end
end

describe Order, quot;calculating taxquot; do
 it quot;should add tax onto the totalquot; do
   fake_calculator = mock('calculator')
   fake_calculator.should_receive(:calculate).
     with(42, quot;CAquot;).and_return 3.26
   o = Order.new quot;CAquot;
   bacon = Item.new quot;Chunky baconquot;, 42
   o.add_item bacon, 1
   o.tax(fake_calculator).should == 3.26
 end
end
Free DI to the rescue
class Order
                                                 Clients continue to
 def tax(calculator=TaxCalculator)
                                                 work unchanged
   calculator.calculate @subtotal, @us_state
 end
end

describe Order, quot;calculating taxquot; do
 it quot;should add tax onto the totalquot; do
   fake_calculator = mock('calculator')
   fake_calculator.should_receive(:calculate).
     with(42, quot;CAquot;).and_return 3.26
   o = Order.new quot;CAquot;
   bacon = Item.new quot;Chunky baconquot;, 42
   o.add_item bacon, 1
   o.tax(fake_calculator).should == 3.26
 end
end
Partial mocking
class Order
 def tax
   TaxCalculator.calculate @subtotal, @us_state
 end
end

describe Order, quot;calculating taxquot; do
 it quot;should add tax onto the totalquot; do
   o = Order.new quot;CAquot;
   bacon = Item.new quot;Chunky baconquot;, 42
   o.add_item bacon, 1
   TaxCalculator.should_receive(:calculate).
     with(42, quot;CAquot;).and_return 3.26
   o.tax.should == 3.26
 end
end
What To Do From Here

Develop a deep understanding of Ruby

Make good use of Rails

Read “Working Effectively with Legacy Code”
by Michael Feathers

Check out our new blog: www.refactorsquad.com
QUESTIONS

More Related Content

What's hot

Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to javaciklum_ods
 
Unit Testing for Great Justice
Unit Testing for Great JusticeUnit Testing for Great Justice
Unit Testing for Great JusticeDomenic Denicola
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everythingnoelrap
 
Intro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSIntro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSJim Lynch
 
vJUG - The JavaFX Ecosystem
vJUG - The JavaFX EcosystemvJUG - The JavaFX Ecosystem
vJUG - The JavaFX EcosystemAndres Almiray
 
The Art of Unit Testing - Towards a Testable Design
The Art of Unit Testing - Towards a Testable DesignThe Art of Unit Testing - Towards a Testable Design
The Art of Unit Testing - Towards a Testable DesignVictor Rentea
 
Dart for Java Developers
Dart for Java DevelopersDart for Java Developers
Dart for Java DevelopersYakov Fain
 
Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015CiaranMcNulty
 
Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3Fabien Potencier
 
Node.JS error handling best practices
Node.JS error handling best practicesNode.JS error handling best practices
Node.JS error handling best practicesYoni Goldberg
 
Painless JavaScript Testing with Jest
Painless JavaScript Testing with JestPainless JavaScript Testing with Jest
Painless JavaScript Testing with JestMichał Pierzchała
 
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019Victor Rentea
 
AngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and JasmineAngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and Jasminefoxp2code
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiRan Mizrahi
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsFITC
 
Unit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSUnit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSKnoldus Inc.
 

What's hot (20)

Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
 
Unit Testing for Great Justice
Unit Testing for Great JusticeUnit Testing for Great Justice
Unit Testing for Great Justice
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
Intro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSIntro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJS
 
Clean Code
Clean CodeClean Code
Clean Code
 
vJUG - The JavaFX Ecosystem
vJUG - The JavaFX EcosystemvJUG - The JavaFX Ecosystem
vJUG - The JavaFX Ecosystem
 
The Art of Unit Testing - Towards a Testable Design
The Art of Unit Testing - Towards a Testable DesignThe Art of Unit Testing - Towards a Testable Design
The Art of Unit Testing - Towards a Testable Design
 
Dart for Java Developers
Dart for Java DevelopersDart for Java Developers
Dart for Java Developers
 
Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015
 
Laravel Unit Testing
Laravel Unit TestingLaravel Unit Testing
Laravel Unit Testing
 
Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3
 
Node.JS error handling best practices
Node.JS error handling best practicesNode.JS error handling best practices
Node.JS error handling best practices
 
Painless JavaScript Testing with Jest
Painless JavaScript Testing with JestPainless JavaScript Testing with Jest
Painless JavaScript Testing with Jest
 
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
 
Unit Testing in iOS
Unit Testing in iOSUnit Testing in iOS
Unit Testing in iOS
 
AngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and JasmineAngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and Jasmine
 
Intro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran MizrahiIntro To JavaScript Unit Testing - Ran Mizrahi
Intro To JavaScript Unit Testing - Ran Mizrahi
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS Applications
 
Unit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSUnit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJS
 
Test
TestTest
Test
 

Viewers also liked

PHP 7.1 : elegance of our legacy
PHP 7.1 : elegance of our legacyPHP 7.1 : elegance of our legacy
PHP 7.1 : elegance of our legacyDamien Seguy
 
Dealing with Legacy PHP Applications
Dealing with Legacy PHP ApplicationsDealing with Legacy PHP Applications
Dealing with Legacy PHP ApplicationsClinton Dreisbach
 
Dealing With Legacy PHP Applications
Dealing With Legacy PHP ApplicationsDealing With Legacy PHP Applications
Dealing With Legacy PHP ApplicationsViget Labs
 
Working with Legacy Code
Working with Legacy CodeWorking with Legacy Code
Working with Legacy CodeEyal Golan
 
Triumph Over Legacy Code with Unit Testing
Triumph Over Legacy Code with Unit Testing Triumph Over Legacy Code with Unit Testing
Triumph Over Legacy Code with Unit Testing Typemock
 
Adding Unit Test To Legacy Code
Adding Unit Test To Legacy CodeAdding Unit Test To Legacy Code
Adding Unit Test To Legacy CodeTerry Yin
 
Unit testing legacy code
Unit testing legacy codeUnit testing legacy code
Unit testing legacy codeLars Thorup
 
Working With Legacy Code
Working With Legacy CodeWorking With Legacy Code
Working With Legacy CodeAndrea Polci
 
Transforming legacy PHP applications with Symfony2 and Varnish
Transforming legacy PHP applications with Symfony2 and VarnishTransforming legacy PHP applications with Symfony2 and Varnish
Transforming legacy PHP applications with Symfony2 and VarnishCraig Marvelley
 
Pimp legacy PHP apps with Apigility - TrueNorthPHP 2014
Pimp legacy PHP apps with Apigility - TrueNorthPHP 2014Pimp legacy PHP apps with Apigility - TrueNorthPHP 2014
Pimp legacy PHP apps with Apigility - TrueNorthPHP 2014Michelangelo van Dam
 
XPDays Ukraine: Legacy
XPDays Ukraine: LegacyXPDays Ukraine: Legacy
XPDays Ukraine: LegacyVictor_Cr
 
XP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy codeXP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy codeDmytro Mindra
 
ITGM#4 Технический долг 2.0
ITGM#4 Технический долг 2.0ITGM#4 Технический долг 2.0
ITGM#4 Технический долг 2.0Maxim Shulga
 
From Legacy to DDD in PHP | Tech Talks | Privalia
From Legacy to DDD in PHP | Tech Talks | PrivaliaFrom Legacy to DDD in PHP | Tech Talks | Privalia
From Legacy to DDD in PHP | Tech Talks | PrivaliaJordi Vila Gallardo
 
Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"Victor_Cr
 
Legacy: как победить в гонке (Joker)
Legacy: как победить в гонке (Joker)Legacy: как победить в гонке (Joker)
Legacy: как победить в гонке (Joker)Victor_Cr
 
Unit testing - the hard parts
Unit testing - the hard partsUnit testing - the hard parts
Unit testing - the hard partsShaun Abram
 
Working Effectively With Legacy Code
Working Effectively With Legacy CodeWorking Effectively With Legacy Code
Working Effectively With Legacy CodeNaresh Jain
 
Refactoring 101
Refactoring 101Refactoring 101
Refactoring 101Adam Culp
 

Viewers also liked (20)

PHP 7.1 : elegance of our legacy
PHP 7.1 : elegance of our legacyPHP 7.1 : elegance of our legacy
PHP 7.1 : elegance of our legacy
 
Dealing with Legacy PHP Applications
Dealing with Legacy PHP ApplicationsDealing with Legacy PHP Applications
Dealing with Legacy PHP Applications
 
Dealing With Legacy PHP Applications
Dealing With Legacy PHP ApplicationsDealing With Legacy PHP Applications
Dealing With Legacy PHP Applications
 
Working with Legacy Code
Working with Legacy CodeWorking with Legacy Code
Working with Legacy Code
 
Triumph Over Legacy Code with Unit Testing
Triumph Over Legacy Code with Unit Testing Triumph Over Legacy Code with Unit Testing
Triumph Over Legacy Code with Unit Testing
 
Automation and Technical Debt
Automation and Technical DebtAutomation and Technical Debt
Automation and Technical Debt
 
Adding Unit Test To Legacy Code
Adding Unit Test To Legacy CodeAdding Unit Test To Legacy Code
Adding Unit Test To Legacy Code
 
Unit testing legacy code
Unit testing legacy codeUnit testing legacy code
Unit testing legacy code
 
Working With Legacy Code
Working With Legacy CodeWorking With Legacy Code
Working With Legacy Code
 
Transforming legacy PHP applications with Symfony2 and Varnish
Transforming legacy PHP applications with Symfony2 and VarnishTransforming legacy PHP applications with Symfony2 and Varnish
Transforming legacy PHP applications with Symfony2 and Varnish
 
Pimp legacy PHP apps with Apigility - TrueNorthPHP 2014
Pimp legacy PHP apps with Apigility - TrueNorthPHP 2014Pimp legacy PHP apps with Apigility - TrueNorthPHP 2014
Pimp legacy PHP apps with Apigility - TrueNorthPHP 2014
 
XPDays Ukraine: Legacy
XPDays Ukraine: LegacyXPDays Ukraine: Legacy
XPDays Ukraine: Legacy
 
XP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy codeXP Days Ukraine 2014 - Refactoring legacy code
XP Days Ukraine 2014 - Refactoring legacy code
 
ITGM#4 Технический долг 2.0
ITGM#4 Технический долг 2.0ITGM#4 Технический долг 2.0
ITGM#4 Технический долг 2.0
 
From Legacy to DDD in PHP | Tech Talks | Privalia
From Legacy to DDD in PHP | Tech Talks | PrivaliaFrom Legacy to DDD in PHP | Tech Talks | Privalia
From Legacy to DDD in PHP | Tech Talks | Privalia
 
Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"Club of anonimous developers "Refactoring: Legacy code"
Club of anonimous developers "Refactoring: Legacy code"
 
Legacy: как победить в гонке (Joker)
Legacy: как победить в гонке (Joker)Legacy: как победить в гонке (Joker)
Legacy: как победить в гонке (Joker)
 
Unit testing - the hard parts
Unit testing - the hard partsUnit testing - the hard parts
Unit testing - the hard parts
 
Working Effectively With Legacy Code
Working Effectively With Legacy CodeWorking Effectively With Legacy Code
Working Effectively With Legacy Code
 
Refactoring 101
Refactoring 101Refactoring 101
Refactoring 101
 

Similar to Working Effectively With Legacy Code

OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialYi-Ting Cheng
 
RSpec User Stories
RSpec User StoriesRSpec User Stories
RSpec User Storiesrahoulb
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on RailsMark Menard
 
Useful Rails Plugins
Useful Rails PluginsUseful Rails Plugins
Useful Rails Pluginsnavjeet
 
Rails antipattern-public
Rails antipattern-publicRails antipattern-public
Rails antipattern-publicChul Ju Hong
 
Rails antipatterns
Rails antipatternsRails antipatterns
Rails antipatternsChul Ju Hong
 
Testing C# and ASP.net using Ruby
Testing C# and ASP.net using RubyTesting C# and ASP.net using Ruby
Testing C# and ASP.net using RubyBen Hall
 
Ride on the Fast Track of Web with Ruby on Rails- Part 2
Ride on the Fast Track of Web with Ruby on Rails- Part 2Ride on the Fast Track of Web with Ruby on Rails- Part 2
Ride on the Fast Track of Web with Ruby on Rails- Part 2A.K.M. Ahsrafuzzaman
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?brynary
 
Ruby on rails
Ruby on rails Ruby on rails
Ruby on rails Mohit Jain
 
Ta Javaserverside Eran Toch
Ta Javaserverside Eran TochTa Javaserverside Eran Toch
Ta Javaserverside Eran TochAdil Jafri
 
SproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsSproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsMike Subelsky
 
Rails 2.3 and Rack - NHRuby Feb 2009
Rails 2.3 and Rack - NHRuby Feb 2009Rails 2.3 and Rack - NHRuby Feb 2009
Rails 2.3 and Rack - NHRuby Feb 2009bturnbull
 
Cache Money Talk: Practical Application
Cache Money Talk: Practical ApplicationCache Money Talk: Practical Application
Cache Money Talk: Practical ApplicationWolfram Arnold
 
Introduction To Code Igniter
Introduction To Code IgniterIntroduction To Code Igniter
Introduction To Code IgniterAmzad Hossain
 
CICONF 2012 - Don't Make Me Read Your Mind
CICONF 2012 - Don't Make Me Read Your MindCICONF 2012 - Don't Make Me Read Your Mind
CICONF 2012 - Don't Make Me Read Your Mindciconf
 

Similar to Working Effectively With Legacy Code (20)

OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
 
RSpec User Stories
RSpec User StoriesRSpec User Stories
RSpec User Stories
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on Rails
 
Useful Rails Plugins
Useful Rails PluginsUseful Rails Plugins
Useful Rails Plugins
 
Jsf Ajax
Jsf AjaxJsf Ajax
Jsf Ajax
 
Rails antipattern-public
Rails antipattern-publicRails antipattern-public
Rails antipattern-public
 
Rails antipatterns
Rails antipatternsRails antipatterns
Rails antipatterns
 
Testing C# and ASP.net using Ruby
Testing C# and ASP.net using RubyTesting C# and ASP.net using Ruby
Testing C# and ASP.net using Ruby
 
Ride on the Fast Track of Web with Ruby on Rails- Part 2
Ride on the Fast Track of Web with Ruby on Rails- Part 2Ride on the Fast Track of Web with Ruby on Rails- Part 2
Ride on the Fast Track of Web with Ruby on Rails- Part 2
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?
 
Ruby on rails
Ruby on rails Ruby on rails
Ruby on rails
 
EPiServer Web Parts
EPiServer Web PartsEPiServer Web Parts
EPiServer Web Parts
 
Ta Javaserverside Eran Toch
Ta Javaserverside Eran TochTa Javaserverside Eran Toch
Ta Javaserverside Eran Toch
 
SproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsSproutCore and the Future of Web Apps
SproutCore and the Future of Web Apps
 
Ruby For Startups
Ruby For StartupsRuby For Startups
Ruby For Startups
 
Rails 2.3 and Rack - NHRuby Feb 2009
Rails 2.3 and Rack - NHRuby Feb 2009Rails 2.3 and Rack - NHRuby Feb 2009
Rails 2.3 and Rack - NHRuby Feb 2009
 
Cache Money Talk: Practical Application
Cache Money Talk: Practical ApplicationCache Money Talk: Practical Application
Cache Money Talk: Practical Application
 
Introduction To Code Igniter
Introduction To Code IgniterIntroduction To Code Igniter
Introduction To Code Igniter
 
CICONF 2012 - Don't Make Me Read Your Mind
CICONF 2012 - Don't Make Me Read Your MindCICONF 2012 - Don't Make Me Read Your Mind
CICONF 2012 - Don't Make Me Read Your Mind
 
Well
WellWell
Well
 

Recently uploaded

New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
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
 
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
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
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
 
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
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
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
 
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
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 

Recently uploaded (20)

New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
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
 
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
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
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
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
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
 
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
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 

Working Effectively With Legacy Code

  • 1. WORKING EFFECTIVELY WITH LEGACY RAILS Pat Maddox & BJ Clark
  • 2. Why Care About Legacy Code? Rails is almost 5 years old Lots of $$ to be made Everyone writes legacy code
  • 3. What is Legacy Code? Code written >3 months ago Code written by someone else
  • 4.
  • 5. What is Legacy Code? Michael Feathers - “Code Without Tests” Ward Cunningham - “Accumulated technical debt” DHH - “Code you wrote when weren’t as good as you are now”
  • 6. Code that is difficult to change
  • 8. What is Technical Debt? “During the planning or execution of a software project, decisions are made to defer necessary work, this is creating technical debt” http://c2.com/cgi/wiki?TechnicalDebt Why is it bad? Not necessarily bad (good vs bad debt)
  • 9. Examples of Technical Debt Rails MediaWiki ActiveScaffold Code you write
  • 10. Recognizing Debt Easy upfront gains Difficult to extend long-term (does the tool support new requirements) No clear migration path
  • 11. Managing Technical Debt “Remove cruft as you go. Build simplicity and clarity in from the beginning, and never relent in keeping them in. “ -Ron Jeffries Big Design Up Front Agile
  • 12. TATFT & RATFT Virtuous cycle - Symbiotic Relationship Anti-pattern: Red. Green. Deploy. Pattern: Red. Green. Refactor. Deploy.
  • 15. Start Testing Now What level? Unit vs Acceptance Cast a wide net
  • 16. Ok, lets test Take 6 weeks off to write tests for your code
  • 17. Ok, lets test Take 6 weeks off to write tests for your code
  • 18. Ok, lets test Just in time Characterization tests Cover existing functionality Never red, always green Test-drive new features
  • 21. Rails already does it Caching has_many :through Delegation, memoization, serialization Validations and the .valid? api
  • 22. Make everything a framework All the benefits of abstracted code Use existing frameworks Write your own mini-frameworks
  • 24. The Tests it quot;works when updated by ownerquot; do describe BoatsController do before(:each) do do_update @owner @owner = User.create! @boat.reload.name.should == quot;SS Minnowquot; @boat = Boat.create! end :name => quot;Pequodquot;, :owner => @owner it quot;works when updated by adminquot; do end admin = User.create! :admin => true do_update admin def do_update(user) @boat.reload.name.should == quot;SS Minnowquot; login_as user end put :update, :id => @boat.to_param, it quot;fails when updated by non-ownerquot; do :boat => nonowner = User.create! {:name => quot;SS Minnowquot;} do_update nonowner end @boat.reload.name.should == quot;Pequodquot; end
  • 25. The model class Boat < ActiveRecord::Base belongs_to :owner, :class_name => quot;Userquot; def can_update?(user) user == owner || user.admin? || owner.blank? end end
  • 26. The controller class BoatsController < ApplicationController def update @boat = Boat.find params[:id] if @boat.can_update?(current_user) if @boat.update_attributes(params[:boat]) redirect_to boat_url(@boat) else render :action => quot;editquot; end else redirect_to boat_url(@boat) end end end
  • 28. The new controller class BoatsController < ApplicationController require_authorization :update def update if @boat.update_attributes(params[:boat]) redirect_to @boat else render :action => quot;editquot; end end def load_model @boat ||= Boat.find(params[:id]) end end
  • 29. The “magic” class ApplicationController < ActionController::Base def self.require_authorization(*actions) actions.each do |a| before_filter :check_authorization, :only => a end end def check_authorization model = load_model unless model.send(quot;can_#{action_name}?quot;, current_user) redirect_to model end end end
  • 30. What we’ve learned Pros Easy to read Behavior for free Cons Not as accessible to brand new Ruby programmers
  • 32. A little cleanup class BoatsController < ApplicationController ... def object @boat ||= Boat.find(params[:id]) end end class ApplicationController < ActionController::Base ... def check_authorization unless object.send(quot;can_#{action_name}?quot;, current_user) redirect_to model end end end
  • 33. resource_controller class BoatsController < ApplicationController resource_controller # thanks James!!! require_authorization :update end
  • 34. Before class BoatsController < ApplicationController def update @boat = Boat.find params[:id] if @boat.can_update?(current_user) if @boat.update_attributes(params[:boat]) redirect_to boat_url(@boat) else render :action => quot;editquot; end else redirect_to boat_url(@boat) end end end
  • 35. After class BoatsController < ApplicationController resource_controller # thanks James!!! require_authorization :update end
  • 37. METAPROGRAMMING ROCKS (with great power comes great responsibility)
  • 39. Rails Extension Points before_*/after_* hooks AR observers ActionController filters
  • 40. The Test describe AccountsController, quot;POST createquot; do def do_post post :create, :account => {:name => quot;Nikequot;} end it quot;should create a project for the accountquot; do do_post Account.first.should have(1).project end end
  • 41. The Controller def create @account = Account.new params[:account] Account.transaction do if @account.save @account.projects.create :name => quot;#{@account.name}'s First Projectquot; redirect_to @account else render :template => quot;newquot; end end end
  • 42. Using Callbacks class Account < ActiveRecord::Base has_many :projects after_create :create_project def create_project projects.create! :name => quot;#{name}'s First Projectquot; end end
  • 43. Simpler Controller def create @account = Account.new params[:account] if @account.save redirect_to @account else render :template => quot;newquot; end end
  • 44. A little more describe AccountsController, quot;POST createquot; do def do_post post :create, :account => {:name => quot;Nikequot;} end it quot;should create a project for the accountquot; do do_post Account.first.should have(1).project end it quot;should create a video for the projectquot; do do_post Account.first.projects.first.should have(1).video end end
  • 45. The Model class Account < ActiveRecord::Base after_create :create_project ... end class Project < ActiveRecord::Base has_many :videos belongs_to :account after_create :create_video def create_video videos.create! :name => quot;#{account.name}'s First Videoquot; end end
  • 46. Tradeoffs Cons Pros Indirection Free transaction semantics Testing is a bit slower/ Skinny controller, fat tougher model
  • 47. Modeling a Business Event class AccountRegistration < ActiveRecord::Base belongs_to :account before_create :setup_account validates_associated :account attr_writer :name def setup_account self.account = Account.create :name => @name project = account.projects.create! :name => quot;#{@name}'s First Projectquot; project.videos.create! :name => quot;#{@name}'s First Videoquot; end end
  • 48. Controller & Other Models # Use resource_controller def create @registration = AccountRegistration.new params[:account] if @registration.save redirect_to @registration.account else render :template => quot;newquot; end end class Project < ActiveRecord::Base has_many :videos end class Account < ActiveRecord::Base has_many :projects end
  • 49. Modeling a Business Event class Account < ActiveRecord::Base class AccountRegistration < AR::Base has_many :projects belongs_to :account end before_create :setup_account validates_associated :account class Project < ActiveRecord::Base attr_writer :name has_many :videos end def setup_account self.account = Account.create :name => @name project = account.projects.create! :name => quot;#{@name}'s First Projectquot; project.videos.create! :name => quot;#{@name}'s First Videoquot; end end
  • 50. SEAMS
  • 51. Seams Modify or sense behavior of code without changing it OO - polymorphism Dependency management
  • 53. class Order def initialize(us_state) @us_state = us_state @subtotal = 0 end def add_item(item, quantity) @subtotal += (item.cost * quantity) end def tax TaxCalculator.calculate @subtotal, @us_state end end describe Order, quot;calculating taxquot; do it quot;should add tax onto the totalquot; do o = Order.new quot;CAquot; bacon = Item.new quot;Chunky baconquot;, 42 o.add_item bacon, 1 o.tax.should == 3.26 end end
  • 54. DI to the rescue class Order def tax(calculator) calculator.calculate @subtotal, @us_state end end describe Order, quot;calculating taxquot; do it quot;should add tax onto the totalquot; do fake_calculator = mock('calculator') fake_calculator.should_receive(:calculate). with(42, quot;CAquot;).and_return 3.26 o = Order.new quot;CAquot; bacon = Item.new quot;Chunky baconquot;, 42 o.add_item bacon, 1 o.tax(fake_calculator).should == 3.26 end end
  • 55. DI to the rescue class Order Breaks existing client code def tax(calculator) calculator.calculate @subtotal, @us_state end end describe Order, quot;calculating taxquot; do it quot;should add tax onto the totalquot; do fake_calculator = mock('calculator') fake_calculator.should_receive(:calculate). with(42, quot;CAquot;).and_return 3.26 o = Order.new quot;CAquot; bacon = Item.new quot;Chunky baconquot;, 42 o.add_item bacon, 1 o.tax(fake_calculator).should == 3.26 end end
  • 56. Free DI to the rescue class Order Clients continue to def tax(calculator=TaxCalculator) work unchanged calculator.calculate @subtotal, @us_state end end describe Order, quot;calculating taxquot; do it quot;should add tax onto the totalquot; do fake_calculator = mock('calculator') fake_calculator.should_receive(:calculate). with(42, quot;CAquot;).and_return 3.26 o = Order.new quot;CAquot; bacon = Item.new quot;Chunky baconquot;, 42 o.add_item bacon, 1 o.tax(fake_calculator).should == 3.26 end end
  • 57. Partial mocking class Order def tax TaxCalculator.calculate @subtotal, @us_state end end describe Order, quot;calculating taxquot; do it quot;should add tax onto the totalquot; do o = Order.new quot;CAquot; bacon = Item.new quot;Chunky baconquot;, 42 o.add_item bacon, 1 TaxCalculator.should_receive(:calculate). with(42, quot;CAquot;).and_return 3.26 o.tax.should == 3.26 end end
  • 58. What To Do From Here Develop a deep understanding of Ruby Make good use of Rails Read “Working Effectively with Legacy Code” by Michael Feathers Check out our new blog: www.refactorsquad.com

Editor's Notes

  1. The length of time doesn&#x2019;t matter. It could be 3 weeks
  2. Code might be unfactored because you weren&#x2019;t good enough to see the refactorings earlier.
  3. No tests - how do you have confidence? You could have well-designed code with no tests... &#x201C;clean room&#x201D;...falcon circling the sky then strikes Bad design Dependencies (Rails makes you not care. Stuff like const_missing is great but hides pain points, association chains)
  4. The more debt you have, the harder it is to adapt to changing requirements. Systems become large, it&#x2019;s important for them to be designed and architected such that you can reason about subsystems. Eventually, programmers want the Big Rewrite. We&#x2019;ve both advocated for the Big Rewrite on a project that hadn&#x2019;t even launched yet.
  5. *** Choosing Rails &#xA0;&#xA0;- Pros: we know them &#xA0;&#xA0;- Cons: Locked into Ruby (issue when you need to have multithreaded code). &#xA0;Opinionated - when your business expands past those opinions, must pay down debt. &#xA0;Example: AR assumes one database. &#xA0;Need to write libraries/rearchitect to support clustering *** Choosing mediawiki &#xA0;&#xA0;- Pros: Easy to get the site up and running &#xA0;&#xA0;- Cons: Difficult to extend, difficult to scale &#xA0;&#xA0;- Result: Spent a year+ replacing it piece-by-piece with Rails *** Using ActiveScaffold &#xA0;&#xA0;- &#xA0;Pros: Get scaffolding quickly and easily &#xA0;&#xA0;- Cons: Internal code is a mess, untested, difficult to extend *** Code you write &#xA0;&#xA0;- Not refactoring / writing tests - Poorly tested code is almost as bad as not testing at all.
  6. has_many => &#xA0;has_many :through &#xA0;(clear migration path) one database server => multiple database servers (requires community to create new tools. no clear migration path out of the box)
  7. ** Could spend six months designing the system so that it supports all the functionality and has extensibility points *** We know that doesn't actually work *** Plus you don't have working software * Agile approach &#xA0;&#xA0;** Do simple things to add value right now &#xA0;&#xA0;** Technical debt is central to Agile development - embrace it
  8. ** TATFT &#xA0;&#xA0;- Testing allows you to refactor &#xA0;&#xA0;- Refactoring pays down debt &#xA0;&#xA0;- Virtuous cycle => Testing makes refactoring possible, refactoring makes testing easier ** RATFT &#xA0;&#xA0;- Antipattern: red/green deploy Just because you have tests for your 70 line controller method, doesn't mean it's good or that you're done. &#xA0;&#xA0;- red/green/REFACTOR deploy &#xA0;&#xA0;- Get to green, take the time to make your code nice. &#xA0;You should spend equal or more time refactoring than making your tests green. AboutUs: 2-3 deployments per day. No staging.
  9. Legacy systems provide existing value. &#xA0;The foremost requirement when making changes to a system is not to lose the existing value. Automated tests provide the safety net.
  10. * You need to write tests, what do you do? Unit Tests or Acceptance Tests? A. Acceptance (originally called functional) Cuke: - Uses full rails stack. - Tests multiple requests in a single test. - Hits multiple models and controllers, session, external services, etc. * What's the point? -&#xA0;1 cucumber test covers the same amount of code as 25 unit tests - Level of abstraction - reasoning about usage of the system, as opposed to one tiny little piece out of context - Captures existing system functionality
  11. Whenever you add code, examine areas that use the code, write tests to exercise those areas (just in time) (Kanban for the ninjas out there) Characterization tests - let you know how the system behaves currently. May even expose bugs but you don&#x2019;t fix them just yet! Make a note or a story in tracker and move on. ** Pushback - Writing tests is too hard. &#xA0;No it's not - No, really, it is too hard
  12. ** Silo code - Push it behind a webservice. &#xA0;Write simple integration tests. &#xA0;Example: AboutUs uses mediawiki as a parsing engine. &#xA0;Easy to write Rails-app level tests for transformations, then push it off to mediawiki service - Don&#x2019;t touch it after you silo it. - Rails 3: mountable apps
  13. ActiveSupport does a looooooot of stuff
  14. ** Use existing frameworks -&#xA0;resource_controller ** Write your own -&#xA0;Pivotal's authorization system (can_create, can_update auto-called from controller) ** Extraction is a very valid refactoring technique.
  15. * How many actions have people written that were like this?
  16. * Added one method (load_model) * Got behavior for free
  17. * Con isn&#x2019;t really a con. New Ruby programmers won&#x2019;t stay new for long * If they learn this, they&#x2019;ll start to write code this way
  18. * I didn&#x2019;t realize how easy that was until I did it from scratch * It &#x201C;just worked&#x201D;
  19. *This is considdered by most people as a &#x201C;clean/skinny&#x201D; controller.
  20. Why refactor? So you can do this We realize that legacy controllers aren&#x2019;t even this clean. We never said it would be easy. Refactor your code, then you can make it sex This is the goal
  21. Transactions in the controller are an anti-pattern.
  22. No explicit transactions You can use a framework Code is tougher to understand due to indirection
  23. Transaction semantics without an explicit transaction Account and project focused on domain responsibilities AccountRegistration provides natural point for stuff like sending a verification email (also helps with testing) AccountRegistration can get sophisticated without muddling model - validates_associated project and video, if you want You could write a script to clean up AccountRegistration records when they&#x2019;re no longer needed, depending on domain
  24. * Problems with this test ** Magic number (california sales tax) ** coupled to TaxCalculator implementation
  25. * Can use a mock or a simpler fake object * Every Java programmer asks &#x201C;what library to use for DI&#x201D; * Ruby programmers say &#x201C;don&#x2019;t use it&#x201D; * Misses the point. Don&#x2019;t use a framework. Use Ruby
  26. * Problem: this will break all clients
  27. Learn the hooks (inherit, included, etc) Understand how has_many works - it&#x2019;s not magic! This lets you be very creative and have fun Working Effectively...gives you concrete strategies for getting a code base under test