Reading
Read the following chapters from The Pragmatic Programmer, anniversary edition (in our library):
- Chapter 1, Topic 3: Software Entropy
- Chapter 2, Topic 9: The Evils of Duplication
- Chapter 2, Topic 10: Orthogonality
- Chapter 5, Topic 28: Decoupling (and the Law of Demeter)
Read the following chapters from Clean Code (in our library):
- Chapter 1: Clean Code
- Chapter 2: Meaningful Names
- Chapter 3: Functions
- Chapter 4: Comments
- Chapter 5: Formatting
- Chapter 8: Boundaries
- Chapter 10: Classes
- Chapter 12: Emergence
- Chapter 17: Smells and Heuristics
Also read:
- How to write modular code
- Keep It DRY, Shy, and Tell the Other Guy Show archive.org snapshot
- GRASP (object-oriented design) Show archive.org snapshot
- Single Responsibility Principle Show archive.org snapshot
- Tell, Don't Ask Show archive.org snapshot
- Best practices for writing code comments Show archive.org snapshot
Read the following chapters from our book Growing Rails Application in Practice:
- 
- Dealing with fat models
 
- 
- Extracting service objects
 
Discuss with your mentor what you took away from each topic.
Rules of thumb
Apart from the concepts mentioned above, there are a many more rules that are good to follow most of the time. For example:
- Embrace Locality: Avoid cluttering one concept across the entire code base. It should be encapsulated in single files or folders.
- 
Be easy to call: A service should always try to have a simple public interface, even if that causes additional complexity within the class. For example:
- Interacting with the model should be easy for controllers and views
- Interacting with helpers and routes should be easy for views
 
- Avoid long instruction manuals: If you need to write down an A4-letter for your colleagues on how to use your service, there might be a way to refactor it to a simpler public interface. A post-it sized note should be enough, if anything!
Exercise
We're building an e-commerce app where users can create and view invoices.
This is our current model:
class Invoice < ApplicationRecord
  has_many :items
  validates_presence_of :recipient_address, :number
end
class Invoice::Item < ApplicationRecord
  belongs_to :invoice
  belongs_to :product
  validates_numericality_of :units
end  
class Product < ApplicationRecord
  validates_presence_of :description
  validates_numericality_of :unit_price
end
This is a view that shows an invoice:
%h1
  Invoice
  = @invoice.number
%h2 Recipient
= @invoice.recipient_address
%h2 Items
%table
  %tr
    %th Description
    %th Quantity
    %th Item total
  - @invoice.items.each do |item|
    %td= item.product.description
    %td= item.units
    %td= item.units * item.product.unit_price
    
  %tr
    %th
      Invoice total
    %td(colspan=3)
      = @invoice.items.sum { |item| item.units * item.product.unit_price } * 1.19
How would you judge the quality of this code?
Try to apply what you learned with a refactoring of the model and the view. What are the advantages of your solution?
Exercise: MovieSearch vs. Authorization
Let's say there is the a MovieSearch class in your project with the following public API:
class MovieSearch
  def initialize(query)
    @query = query
  end
  
  def results
    Movie.where('title LIKE ?', @query)
  end
end
search = MovieSearch.new('Interstellar')
search.results.each do |movie|
  puts movie.title
end
Now the current user should not be allowed to search all movies, but only a subset based on their role and the movie's state. A naive inline authorization could look like this:
class MovieSearch
  def initialize(query, current_user)
    @query = query
    @current_user = current_user
  end
  
  def results
    scope = Movie.where('title LIKE ?', @query)
    unless current_user.moderator?
      scope = scope.where('state = "approved" OR user_id = ?', @current_user.id)
    end
    scope
  end
end
Try to come up with an alternative implementation where the two concepts (movie search and authorization) are less coupled.