Form should follow function - or: Why RPC really is bad for your mental health

What is a subroutine call like the following?

int r = Calc(a, b);

It is an abstraction and a promise!

It is an abstraction of something like this

push a
push b
call 1234
pop r

The 3GL syntax hides all the low level details like allocating a stack frame, pushing actual parameters on the stack, jumping to the subroutines address, and later retrieving its result from the stack. Maybe even no stack is used at all. Maybe the parameters are passed in registers. In order to solve your customers problem you don´t want to be concerned with all this nitty gritty detail and any machine dependencies.

In addition the subroutine is a promise to return a result pretty fast. It says, "Hey, you´ll get the requested result in just a moment. I´ll be back in a hurry. It´s worthwhile to wait right here." This promise is implicit, though. The usual notation like above does not contain this promise. Rather it stems from your practice with such kind of subroutines. And you become aware of it whenever a subroutine takes an overly long time to return and thus hampers the performance of your program.

Form follows function promise

Now, let´s look at a couple of other well known abstractions and promises:

What are the abstractions? Instead of doing the switching of lines you use a telephone and dial a number. Instead of growing vegetables and milking cows yourself, you go to the supermarket. Instead of going going to a theater play or produce a movie yourself you switch on the TV. (Yeah, I know, maybe the "abstractions" are not 100% correct. But in any case, these tokens of civilization hide uncomfortable details, don´t they? They make life easier for you, don´t they? Since making something easier is the purpose of abstraction, I´d say they are abstraction. Ok?)

And what are the promises? The phone promises a direct link and fluent conversation with your beloved ones. With today´s mobile phones even "... whereever and whenever you like!" is appended to it. The super market promises fresh food and a huge choice almost 24x7. And the TV promises at least moving pictures - sometimes even matching your current mood.

This all great, I´d say. We love these abstractions and promises. And we are very used to their form. Maybe we even love their form, because it´s so easy to use. Their form therefore is a representation of their services and promises we are very familiar with. Or to turn it around: Whenever we encouter the form, we immeditately associtate a certain promise with it.

Now, what happens, if the promise is not fullfilled? We are disappointed. If you want to talk to your friend on the phone to impart some exicting news and keep hearing just her answering machine, you are frustrated. If you go to the super market and they don´t carry the type of bread or beer, you are frustraed. If you switched on the TV and all you could see were interviews or stills of an exciting event, you´d be frustrated.

If some technical gadget or service does not keep its promise... you´d rather like to know so you can switch to an alternative that´s better suited to what you want or what is available.

If communication via phone degrades to leaving messages on answering machines, you probably will resort to letters, faxes or emails as means for asynchronous communication. If a super market does not provide fresh food next to beer and next to magazines you probably prefer to get really fresh vegetables at a local farmers´ market with the added chance to actually bargain your price. And if a TV program deteriorates to pretty pictures, why not look at them in a nice book at any time and at any place and as often as you want?

Letters, farmers´ markets,  and books hold different promises than phones, super markets, and TVs. So their form is different, it´s optimized for their kind of services.

Why am I talking about phones, letters, books in a posting on software? Because I believe in "form follows function" - and I guess, you do so too. What strikes me odd, then, is how little we care about form when it comes to communication in today´s distributed software.

To say it bluntly: If a subroutine call is the accepted form for fullfilling the promise of swift and synchronous execution of a request, then, why would anyone use the same form, if this promise cannot be kept?

I have to admit, for a long time I liked the programming model of .NET Remoting and RPC-style Web services, or even the former DCOM. But just recently I came to the conclusion that using the syntax of subroutine calls is fundamentally wrong whenever the promise that´s tied to it cannot be kept, i.e. when what happens under the hood is completely different.

I´m not just talking about switching to message oriented "thinking" when designing distributed software. I´m talking about the need for actually seek appropriate manifestations for communication.

If you look at a phone or book you immediately know what kind of service you can expect from it. Also you have a pretty clear idea of the quality of the service.

RPC is the wrong form

When you look at a subroutine call, though, you might have an idea of the service, the subroutine´s function - but nowadays you cannot really know anything about the non-functional aspects of the service. Will it keep the promise of the subroutine call syntax and return fast? Will the service execute synchronously or asynchronously? Will the service run in the same address space, on the same thread, or on a different machine in a far away country? Can I be sure the request reaches the service worker at all? Is the service worker available? Can I be sure to receive the result?

By looking at

int r = myservice.Calc(a, b);

you can´t answer any of these questions. That´s what I find fundamentally wrong with today´s communication offerings - even with WCF. Because it does not really make a difference if I write the above or the following:

RequestMessage req = new RequestMessage(a, b);
ResponseMessage resp = myservice.Calc(req);
int r = resp.result;

The latter form is only slightly different. It´s still a subroutine call promising at least guaranteed, synchronous and immediate processing of my request.

So what I want to say is:

Whenever the promise of a subroutine call syntax - guaranteed, synchronous and immediate processing - cannot be kept by a service, then don´t use subroutine call syntax for communication.

You´re asking why? Well, it´s bad for your mental health ;-) Mental health, I´d say, depends on trust. The less you can trust an environment, the more uneasy you feel - leading to paranoia in extreme cases. Trust is fundamental for your wellbeing. And trust starts with looking at something and immediately be able to categorize it, to infer its properties. But if you look at the above subroutine call you cannot infer its properties, which leads to a lack of trust. You don´t necessarily get what you see. And that´s bad - as you might also understand if you´re a proponent of WYSIWYG ;-)

Fundamental for productive and healthy communication is to be clear and outspoken. Say what you mean, make obviouse what you can do and cannot do. And I think that´s also true for any communication related with software. For example that´s the reason everybody likes understandable and informative subroutine names.

If you subscribe to that, then you should understand why I think RPC remote (or non-stack based) communication mostly (or maybe even always) is bad for your mental health. It violates the above basic requirements for any communication because it suggests non-functional properties it mostly does not have.


# re: Form should follow function - or: Why RPC really is bad for your mental health

Friday, February 24, 2006 6:33 PM by WalterCReel

"don´t use subroutine call syntax for communication." is a bit of a stretch.

How is this new-fangled idiom of "service oriented" calls any different in premise than a recv()?

A big hint to the client programmer that the contract of "guaranteed, synchronous and immediate processing" is going to be broken is that the type of call is explicit in the example. The 'myservice' portion of:

int r = myservice.Calc(a, b);

should be a clue.

As David said, it's up to the programmer of 'myservice' to know to 'raise' or 'throw' when things go awry so that the client programmer can just treat the service method as a regular funtion call unless an exceptional situation has occurred (like a timeout).

If we're worried about blocking then the programmer should know to use asynchronous facilities (when available).

When someone only has a hammer you can't blame them for making everything look like a nail.