0
Restructuring Rails
Eric Marthinsen
CTO and Founder, Agile Commerce
A Refactoring Story
Overview
•CTO and Founder of Agile
Commerce.
•Part of team that bought Sortfolio
from 37signals in 2012.
•Have been enhanc...
How was
their code?
Rule
Stable code running a
profitable company is
always successful code.
However
•Sortfolio was a successful MVP.
•37signals’ testing philosophy differs
from my own.
•Rails 2.3.8
•Needed some work...
Refactoring Result
•Improved from a CodeClimate
score of 2.78 to 3.51.
•Drastically improved test coverage,
while decreasi...
Today’s Objective
Share the principles I used to
improve the Sortfolio codebase in a
way that you can apply these
principl...
Reduce callbacks
Embrace CSS3
Inline concerns
Add decorators
STI
Create services
Create gateways
Custom presenters
DRY
Con...
Extract Class
Benefits
•Better domain model
•Easier comprehension
•More granular tests
•Faster tests
•More hooks for mocking
•Overall, be...
My Thesis
I believe that having classes with too
many responsibilities is the primary
cause of poor object-oriented code.
...
Why Doubly True?
•ActiveRecord combines data and
business layers.
•View code often leaks into models
and controllers.
•Hel...
“Fat model,
skinny controller”
Wrong - everything
should be skinny.
Example: Search
Step 0: Start
class Listing < ActiveRecord::Base
# associations
# validations
def self.get_pro_listings(location_id, budge...
Object-Orientation
•An object is data and the methods
that operate on that data.
•An object should represent a single
thin...
Problems
•Why does a listing know how to
find its peers?
•We’re attaching collection methods
to a type declaration.
•A doma...
Step 1
class Listing < ActiveRecord::Base
# associations
# validations
end
class ListingIndex
def self.get_pro_listings(lo...
Progress
•Now expressing the collection of
Listings in the domain model.
•Creating a “preferred” interface.
•But, not DRY,...
Step 2
class Listing < ActiveRecord::Base
# associations
# validations
end
class ListingIndex
def self.get_listings(is_pro...
Progress
•ListingIndex is now DRY
•Still a couple domain concepts
hidden in there.
•Adding arguments is not scalable.
Step 3
class Listing < ActiveRecord::Base
# associations
# validations
end
class ListingIndex
def self.search(query)
# les...
Progress
•We’ve teased out another domain
concept.
•Queries are now scalable and easy
to augment.
•ListingIndex has an int...
Step 4
class ListingIndex
def self.search(query)
# less code
end
end
class ListingQuery
def initialize(options)
# set prop...
Progress
•Results are now expressed in the
domain model.
•Big improvements to other areas of
the code.
•Set up for success...
Step 5: Final
class ListingIndex
def self.search(query)
# less code
end
def self.add_listing(listing)
# trivial
end
def se...
Results
•Listing was split into Listing,
ListingIndex, ListingQuery, and
ListingResults.
•Better design allowed for trivia...
Takeaways
Your app probably
has fewer classes
than it should.
It’s easier to
combine classes
than to separate
them.
Models don’t need
to inherit from
ActiveRecord::Base
Conventions don’t
always apply.
More Info
Eric Marthinsen
emarthinsen@agilecommerce.com
www.agilecommerce.com/blog
@agilecommerce
@ericmarthinsen
THANKS!
Upcoming SlideShare
Loading in...5
×

Restructuring rails

459

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
459
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "Restructuring rails"

  1. 1. Restructuring Rails Eric Marthinsen CTO and Founder, Agile Commerce A Refactoring Story
  2. 2. Overview •CTO and Founder of Agile Commerce. •Part of team that bought Sortfolio from 37signals in 2012. •Have been enhancing it for about a year.
  3. 3. How was their code?
  4. 4. Rule Stable code running a profitable company is always successful code.
  5. 5. However •Sortfolio was a successful MVP. •37signals’ testing philosophy differs from my own. •Rails 2.3.8 •Needed some work to prepare for new features.
  6. 6. Refactoring Result •Improved from a CodeClimate score of 2.78 to 3.51. •Drastically improved test coverage, while decreasing test run time. •Doubled, roughly, our development velocity.
  7. 7. Today’s Objective Share the principles I used to improve the Sortfolio codebase in a way that you can apply these principles to your own projects.
  8. 8. Reduce callbacks Embrace CSS3 Inline concerns Add decorators STI Create services Create gateways Custom presenters DRY Config to ENV ElasticSearch Method extraction Fewer static methods Extract class
  9. 9. Extract Class
  10. 10. Benefits •Better domain model •Easier comprehension •More granular tests •Faster tests •More hooks for mocking •Overall, better software
  11. 11. My Thesis I believe that having classes with too many responsibilities is the primary cause of poor object-oriented code. This is true of any object-oriented platform, but doubly true of Rails.
  12. 12. Why Doubly True? •ActiveRecord combines data and business layers. •View code often leaks into models and controllers. •Helpers are dumping grounds for procedural code. •Rails conventions support overburdened objects.
  13. 13. “Fat model, skinny controller” Wrong - everything should be skinny.
  14. 14. Example: Search
  15. 15. Step 0: Start class Listing < ActiveRecord::Base # associations # validations def self.get_pro_listings(location_id, budget_id, start, offset) # lots of code end def self.get_free_listings(location_id, budget_id, start, offset) # lots of similar code end end
  16. 16. Object-Orientation •An object is data and the methods that operate on that data. •An object should represent a single thing. •A class defines the type of the object.
  17. 17. Problems •Why does a listing know how to find its peers? •We’re attaching collection methods to a type declaration. •A domain concept is being hidden.
  18. 18. Step 1 class Listing < ActiveRecord::Base # associations # validations end class ListingIndex def self.get_pro_listings(location_id, budget_id, start, offset) # lots of code end def self.get_free_listings(location_id, budget_id, start, offset) # lots of similar code end end
  19. 19. Progress •Now expressing the collection of Listings in the domain model. •Creating a “preferred” interface. •But, not DRY, and two domain concepts remain unexpressed.
  20. 20. Step 2 class Listing < ActiveRecord::Base # associations # validations end class ListingIndex def self.get_listings(is_pro, location_id, budget_id, start, offset) # lots of code end end
  21. 21. Progress •ListingIndex is now DRY •Still a couple domain concepts hidden in there. •Adding arguments is not scalable.
  22. 22. Step 3 class Listing < ActiveRecord::Base # associations # validations end class ListingIndex def self.search(query) # less code end end class ListingQuery def initialize(options) # set properties end end
  23. 23. Progress •We’ve teased out another domain concept. •Queries are now scalable and easy to augment. •ListingIndex has an intuitive #search method. •Still one domain concept hiding in there.
  24. 24. Step 4 class ListingIndex def self.search(query) # less code end end class ListingQuery def initialize(options) # set properties end end class ListingResults # mostly attrs end
  25. 25. Progress •Results are now expressed in the domain model. •Big improvements to other areas of the code. •Set up for success and making big changes behind a stable interface.
  26. 26. Step 5: Final class ListingIndex def self.search(query) # less code end def self.add_listing(listing) # trivial end def self.remove_listing(listing) # trivial end end class ListingQuery # no change end class ListingResults # mostly attrs end
  27. 27. Results •Listing was split into Listing, ListingIndex, ListingQuery, and ListingResults. •Better design allowed for trivial introduction of ElasticSearch. •Much easier and faster to test.
  28. 28. Takeaways
  29. 29. Your app probably has fewer classes than it should.
  30. 30. It’s easier to combine classes than to separate them.
  31. 31. Models don’t need to inherit from ActiveRecord::Base
  32. 32. Conventions don’t always apply.
  33. 33. More Info Eric Marthinsen emarthinsen@agilecommerce.com www.agilecommerce.com/blog @agilecommerce @ericmarthinsen THANKS!
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×