Made Tech Blog

Tell, don’t ask. It’s a matter of principle.

When we design software with maintainability in mind, one obvious goal is to keep the unnecessary lines of code to a bare minimum. The less you need to see/read that is not vital to your understanding of the domain model, the better.

You want your domain model to be free from too much implementation knowledge. By implementation knowledge I mean things that do not deal directly with the problem that you are trying to solve. A connection to a database is an example of this. It is an implementation detail that helps you achieve your goal, but is not your actual goal or the goal of your clients.

If this type of knowledge is not abstracted away (Information Hiding), it only serves to detract from understanding of the system. You end up having to keep more of the program in your own memory to solve a particular problem. At a certain point bugs become inevitable because the human memory is actually pretty bad.

What is the Tell Don’t Ask principle?

Very briefly, the Tell Don’t Ask principle states that you should tell an object what to do and not query it for data, and then make decisions based on the answer you get back.

In object oriented languages we like to focus on the messages passing between objects, and sending instructions to an object is better than querying it, and then altering it’s state based on the outcome. The latter is how procedural, highly coupled disasters spring into existence.

This style of Tell Don’t Ask promotes both the encapsulation and cohesiveness of your objects and you end up with a less coupled, more pluggable system. A system that is not tightly coupled is less resistant to change, easier to maintain, and easier to test.

If your code already follows the Law of Demeter (objects only communicate with objects that are close to them on the object graph), it will be easier to apply the Tell Don’t Ask principle.

Here is an example of code that does not follow the Tell Don’t Ask Principle:

class StockManager   
  def self.get_stock_purchased     
    stock_purchased_database_query  
  end   

  def self.get_stock_sold
     stock_sold_database_query   
  end
end

class DisplayManager   
  def display     
    stock_purchased = StockManager.get_stock_purchased(product)
    stock_sold = StockManager.get_stock_sold(product)
    puts stock_purchased.to_i - stock_sold.to_i   
  end
end

DisplayManager knows too much about how the stock on hand is calculated. Information from StockManager has leaked out and is not encapsulated. We also have less abstractions which name and clarify our intentions.

Here is code that follows the Tell Don’t Ask principle. The knowledge of stock is kept inside the object.

class StockManager  
  def self.get_count_on_hand    
    get_stock_purchased - get_stock_sold  
  end  

  private    
  def self.get_stock_purchased      
    stock_purchased_database_query    
  end    

  def self.get_stock_sold
    stock_sold_database_query    
  end
end

class DisplayManager  
  def display    
    puts StockManager.get_count_on_hand  
  end
end

This is a very basic example of where we put the logic with the data, and it has many benefits. It also promotes Composite Simpler Than the Sum of Its Parts and the Law of Demeter.

The Tell Don’t Ask principle has been known to conflict with the Single Responsibility Principle (which I think ranks higher in value), and objects can grow to become large if not used carefully. As with most principles/laws in software design, this is not set in stone and is also not a silver bullet to solve all your problems.

However, by following the Tell Don’t Ask principle and pushing complexity down to lower levels, you end up with a system that is much easier to reason about.

About the Author

Avatar for Emile Swarts

Emile Swarts

Lead Software Engineer at Made Tech

All about big beards, beers and text editors from the seventies.