Periodically, people try to distill the essence of object-orientation. They come up with rules of thumb that pack a powerful punch. One of these is “Tell, Don’t Ask.” The Pragmatic Programmers came up with that phrase and it’s a great piece of advice. When possible, prefer to tell an object to do something rather than asking it for some data. If you ask an object for some data, massage it, and then pass it back to the same object, you’ve found an operation which probably belongs on that other object.
Rules of thumb are great, and no one expects them to apply every place, but I’ve seen some interesting things when I’ve tried to push “tell, don’t ask" to the extreme. I end up with pipelined architectures. Data comes into the system and it’s processed by an object. The object sends a message to another object, and eventually you end up with some event or action that is produced by the last set of objects in the call chain. This sort of design works, but it’s definitely not the way you'd tackle most design problems unless you're working in shell.
I was reminded of this sort of architecture a while ago when I was looking at some code that Steve Freeman and Nat Pryce have been using as examples in their JMock2 tutorials. A common theme around their work is that correct use of mocks in test-driven development can lead to a rather different style of design. Rather then telling an object to do something and then asking whether it was done, you tell an object to do something and then see what happens to its collaborators. This leads to designs that are somewhat like what I described above. They are a bit like a workflow. In conversations about larger systems, Steve tells me it’s better. There’s less design impact under new requirements. And, it makes sense. Each object is responsible for doing one thing and notifying its successor. That’s a bit less than what you have when your objects are responsible for doing things as well as coordinating interaction with objects that give them results.
No, this “push” architecture is a bit different. It’s almost the opposite of functional programming. In the purest form of functional programming, you never tell; you ask. And when you have lazy evaluation, the system only does as much as it really needs to when it’s answering your question. But, despite the differences, there are similarities between these “push” and “pull” systems. Both of them seem to have a relatively stateless substrate that data flows along.
So, there’s this confluence between “tell, don’t ask” and a particular approach to TDD. What’s happening here? I think it comes back something central. Return values push work back on callers. It's not enough to do some work and decide to send a message to a collaborator; you have wait for something to come back to you and then, perhaps, do some more work. We're better off not returning values.
We can carry it a bit further also. If we aren't returning values, maybe we're better off sending asynchronous messages send rather than synchronous messages? If we aren't waiting for a return values, why not?
Curiously enough, this model is somewhat like the one used by Erlang. Erlang has developed quite a buzz recently. It presents a concurrency model that is almost entirely different from the norm. In Erlang, creating new processes is cheap. It might even be cheaper than creating objects in OO languages. The idea behind Erlang is that if you can make a large number of processes and guarantee that they never share state, you can develop more robust systems. Each process receives messages, does its work, and sends messages to other processes. The message sends are largely asynchronous. Sounds familiar, doesn’t it?
Ralph Johnson has written that Erlang is really OO at its core. Processes are objects. They are little processing hubs that do work and send results to their neighbors. In a way, this is much closer to the metaphor of objects than we’re used to. Alan Kay has mentioned that messaging is primary to OO, that the metaphor is cells in an organism. Cells send chemical messages to other cells, and I’m no biologist, but I doubt they are synchronous.
I nearly didn't post this blog tonight. It's terribly abstract, and it sort of flies off in several directions. Seriously, I can only think about this sort of thing for so long before I'm ready to knock myself in the head just to reconnect with something tangible, but it is nice to mull some of these things. You never know what you'll end up with.
If you have time, try the extreme "tell, don't ask" experiment. Try to write a little system using only methods with no return values. If you do, ask yourself afterwards how you really decide whether to ask or tell in your designs. There's a lot to learn there.

Comments (18)
You can tell an object to do something, but what if the object can't do it? You need to get some feedback, catching an exception is a good way to do it. So the phrase should be "tell, don't ask, then be prepared when it all goes wrong"
Posted by midget1 | October 31, 2007 9:43 AM
Nice article. Not sure yet whether using so many small objects is actually good.
Why not just use a general message object that takes care to pass the data between these objects?
That object can act as (a virtual) slot-reciever and send-messager. It sounds as if it adds a tiny bit complexity, but in my experience it simplifies a lot when you dont at all have to worry about data, or messages, and instead delegate this to this object.
About Erlang...
I must however say that my personal observation was more that Haskell is in real hype, not Erlang. But i may be wrong and biased (since I do use mostly Ruby since years).
(I dont have anything against Erlang, just to make that clear.)
Posted by she | October 31, 2007 9:51 AM
Continuation-passing style is basically methods with no return values; that doesn't feel very OO.
Posted by Anonymous | October 31, 2007 9:53 AM
I've been playing with this style for quite a bit, and it's an interesting approach.
However, there are a few issues I consistently run in that tell me the extreme isn't quite right either.
#1 - Error cases. If you always tell and never ask, you've reduced all error cases to exceptions - your objects have no way to communicate back directly.
Or, alternatively, really tight coupling where all your objects know the big error handler in the sky. Which brings up the question if the messaging system doesn't need to be extended in a way to allow anonymous recipients/broadcast messages
#2 Blind data. (I.e. whenever you're writing a data store. Or if you're interfacing with a non-OO store)
Posted by Robert 'Groby' Blum | October 31, 2007 10:22 AM
"Tell, don't ask" taken to the extreme sounds very much like CPS (continuation Passing Style) or Data Flow programming.
I think it is definitely a good idea... although I would say in it's current incarnation, it can be a bit tedious.
Posted by Tyler Prete | October 31, 2007 1:40 PM
The real-time OO has based all its work in the asynchronous messaging paradigm. One of my favorite authors on the matter, H.S. Lahman, claims that people assume OO calls are synchronous, but that is accidentally, because it was easier for the 3GL to implement it that way, but the messages should be announcements to other objects ("I'm done!") instead of "do this".
Sure he'll explain it better than me:
http://pathfinderpeople.blogs.com/hslahman/index.html
Posted by Gabriel C | October 31, 2007 4:30 PM
Alan Kay once said 'And I believe that every object on the internet should have an IP ... that objects basically are like servers.' (see http://tekkie.wordpress.com/2007/07/11/redefining-computing-part-2/ ). So I think he seems to have similiar ideas (though I don't know how he thinks about Erlang).
Posted by Hendrik Lipka | October 31, 2007 4:45 PM
"Ralph Johnson has written that Erlang is really OO at its core."
"Joe Armstrong ... The thing that bugs me about the book (and about his talks) is that he make more fuss than he should about the functional language aspect of Erlang and not enough about the OO aspect. In fact, he denies that it is OO."
It's funny how we select the few aspects that help our argument and ignore those that don't - 10 or 15 years ago we'd argue that such and such a language couldn't possibly be OO because it was missing features X and Y, now faced with an interesting language we're happy to argue that of course it's really OO even though it's missing features X and Y :-)
Here's the obvious, there is nothing, nothing, OO about sequential Erlang - it's a functional programming language.
In concurrent Erlang, processes have identity like Objects and like Unix processes; and Erlang processes use message-passing for interprocess communication, and then within each process were back to functional programming.
Erlang processes are of course (small o) objects - does that make Erlang an Object Oriented Programming Language?
Posted by Isaac Gouy | October 31, 2007 5:55 PM
I'm glad you did post it, it's one of the most interesting thoughts on programming I've seen for some time. Thanks.
Posted by Justin | October 31, 2007 6:22 PM
Functional Reactive Programming separates things along roughly these lines. Functional within an object, asynchronous messaging between objects. A bit like Monads shift state to the top-level of your program and let you forget it lower down.
Posted by Greg M | November 1, 2007 2:40 AM
> ...but it's definitely not the way you'd tackle most design problems unless you're working in shell.
First of all, why not? The shell way of doing things works great for me. Why not have more of that style of programming elsewhere?
And is a shell pipeline a push or pull system? It's neither. The steps in the pipe are just connected to each other. This shows that the push or pull semantics are just a technicality. A limitation of current environments. When things work right you don't need to think about it most of the time.
I think Doug McIlroy had the right idea all along.
Posted by Oren Tirosh | November 1, 2007 2:44 AM
I just realized that "Tell don't ask" is what Oleg Kiselyov is trying to say in Towards the best collection traversal interface perhaps not so succintly, albeit with gobs more technical detail.
Midget1 and Groby mentioned the issues of exceptions/error cases. Oleg addresses them in the "premature termination" capability of his higher-order Tell method which he calls an enumerator.
Posted by Kefer D | November 1, 2007 7:52 AM
I agree with the pointer to dataflow or flow-based programming. Java developers that are having to build high-performance data processing or data analysis Java apps should look at our beta framework at http://www.pervasivedatarush.com
It is a Java framework for data-intensive applications (not transactional like J2EE, but rather bulk data management) that handles horizontal, vertical and pipeline parallelism on multicore platforms. You don't have to use any Java NIO or concurrency APIs -- rather, it uses the dataflow (wikipedia.org/wiki/Dataflow) or flow-based computing (wikipedia.org/wiki/Flow-based_programming) approach.
There's a free download of the framework at the website. I look forward to comments on this blog.
Thanks!
Posted by Steve Hochschild | November 1, 2007 5:07 PM
midget1, Robert:
I agree that it can be awkward, but one of the things that I like to do when designing is build areas of code that don't have to care about error detection and recovery. The detection is pushed to the edges of the system. It's not a panacea, but I mention it because I can't imagine myself programming in this style and having catch handlers all over the place.
Posted by Michael Feathers | November 5, 2007 8:20 AM
Tyler: Thanks, I'll have to look at CPS
Posted by Michael Feathers | November 5, 2007 8:21 AM
This post presents an interesting perspective on OOD. I've given it some thought, and I think if we wanted to be faithful to what Alan Kay was talking about, we'd recast messages in a slightly different way.
Kay uses the term "goals" for messages. So "tell" is closer to that than "ask", but this suggests programming in an imperative way, which I don't think is what Kay is after. A "goal" is an objective. So a message represents something an object can do, a state it can get to, or something it can produce when it's invoked. Sometimes goals involve other objects as well.
I read somewhere that a rule of thumb is that objects are nouns, messages are verbs, and that parameters in messages can be nouns or adjectives.
A couple English examples I've seen Kay use to illustrate OOP is:
"The boy runs down"
and,
"The clock runs down"
"boy" and "clock" are the objects, "runs" is the verb (message) used in both (having different meaning in each), and "down" is I guess the adjective "parameter". So you're not telling the boy to run down. The term "runs" tells you something about what the boy and the clock can do. The English example might be too imprecise for programming, but the point is the meaning of what you're designing. The point is not necessarily to create commands for objects.
In a larger context of object design, the word "goal" evokes for me a meaning of "This is what this thing is about", or "what it can do". It suggests that the object can describe its purpose to you, and you can use those descriptions to make things happen. I haven't asked Kay about this, but maybe he's talking about self-fulfilling prophecies, in a way. So when you say, "boy runs: down", you see it happen, instead of "boy run: down".
I agree that asking questions doesn't really fit in this model, and should probably be avoided, but I think we should have a more expanded definition than "tell".
Posted by Mark Miller | November 5, 2007 7:53 PM
When pushing Tell, Don't Ask really hard in an OO design, I've found that, like a commenter says above, most tells actually end up as event announcements. The few remaining dependencies are used to pull data into an object from a query. When you reflectively map objects to dumb data (ORM, mapping to/from GUIs, serialisation to messages, etc.), the design needs very few queries. The few that are left are used to filter or select objects in collections.
I've been experimenting with a programming style in which objects do not even have references to one another and can only broadcast messages and subscribe to messages by content. In this case, objects themselves don't have identity, which makes some design patterns difficult to implement or entirely irrelevant, but also lets objects share responsibility for processing requests without their clients being aware of it.
The difficulty with a heavily multicast, event-based design is that the architecture (the actual connections between objects) is implicit and fluid, However, because objects in this model must advertise and subscribe to events, the connections can be discovered at run-time.
Posted by Nat Pryce | November 13, 2007 5:41 PM
You've just described XSLT.
Posted by M. David Peterson | November 14, 2007 10:29 PM