Restructuring rails

Download Restructuring rails

Post on 11-May-2015

416 views

Category:

Technology

1 download

Embed Size (px)

TRANSCRIPT

<ul><li>1.Restructuring Rails Eric Marthinsen CTO and Founder, Agile Commerce A Refactoring Story </li></ul> <p>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. How was their code? 4. Rule Stable code running a protable company is always successful code. 5. However Sortfolio was a successful MVP. 37signals testing philosophy diers from my own. Rails 2.3.8 Needed some work to prepare for new features. 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. Todays 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. Reduce callbacks Embrace CSS3 Inline concerns Add decorators STI Create services Create gateways Custom presenters DRY Cong to ENV ElasticSearch Method extraction Fewer static methods Extract class 9. Extract Class 10. Benets Better domain model Easier comprehension More granular tests Faster tests More hooks for mocking Overall, better software 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. 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. Fat model, skinny controller Wrong - everything should be skinny. 14. Example: Search 15. Step 0: Start class Listing &lt; 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. Object-Orientation An object is data and the methods that operate on that data. An object should represent a single thing. A class denes the type of the object. 17. Problems Why does a listing know how to nd its peers? Were attaching collection methods to a type declaration. A domain concept is being hidden. 18. Step 1 class Listing &lt; 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. 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. Step 2 class Listing &lt; 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. Progress ListingIndex is now DRY Still a couple domain concepts hidden in there. Adding arguments is not scalable. 22. Step 3 class Listing &lt; 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. Progress Weve 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. 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. 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. 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. 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. Takeaways 29. Your app probably has fewer classes than it should. 30. Its easier to combine classes than to separate them. 31. Models dont need to inherit from ActiveRecord::Base 32. Conventions dont always apply. 33. More Info Eric Marthinsen emarthinsen@agilecommerce.com www.agilecommerce.com/blog @agilecommerce @ericmarthinsen THANKS! </p>