Restructuring rails

860 views

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
860
On SlideShare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

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!

×