Back to Course

Spot the Tax · Card 9 of 20

Depend on the smallest interface that does the job

Why a service that demands a full User can't be reused with a Lead or a Contact.

The code

What will this cost you in six months?

class ReportPdfService
  def initialize(user)
    @user = user
  end

  def generate
    pdf.text "Report for #{@user.full_name} (#{@user.email})"
    pdf.text "Generated #{Time.current}"
  end
end

# Two months later, marketing asks:
# "Can we generate this report for a Lead before they sign up?"
# Lead has full_name and email. But it isn't a User.

The problem

The service signature says it takes a User, but if you actually look at what it uses, it only ever calls full_name and email. Nothing else. So when marketing comes back two months later and asks if you can run the same report for a Lead — which has those exact two attributes — you can't, even though it would technically work fine. The service is coupled to the User class even though it never needed any of the things that make a User a User.

Take a moment. Before revealing, ask yourself what the service actually needs from the user it's passed. Could you express that more narrowly?