When Andy first asked me to write on "Beautiful Code" just two lines of Perl immediately sprang to mind.

    use LWP::Simple;
    my $page = get("http://www.google.com");

I see beauty in this code at many layers. Let's start with the first, the beauty of its simple interface.

Whether or not you know Perl, or even know how to program, you can figure out what that code does and that is beautiful. Why is that code so readily obvious to anyone who's used a web browser? Because the Gulf of Execution is so narrow.

The Gulf of Execution is the difference between the user's goal, stated in the user's terms, and the actions needed to achieve that goal. Here the user has a simple goal, "get a web page". A simple goal should be accomplished by simple actions related to that goal. With a web browser the user simply types in the URL and clicks a button. There's no reason why code should be any different.

This code accomplishes the user's goal with just two actions:

  1. Load LWP::Simple.
  2. Call get() with the desired URL.

One line of scaffolding, code which is just setup for the real action, brings in everything necessary to accomplish what the user wants in a neat package. One line of code gets them their web page as simple text, ready to be munged however the user likes. The final action is nearly a reiteration of the goal itself.

Compare with how you're supposed to do this long-hand:

     use LWP::UserAgent;

     my $ua = LWP::UserAgent->new;
     $ua->agent("MyAgent/0.01");

     my $req = HTTP::Request->new(GET => 'http://www.google.com');
     $req->header('Accept' => 'text/html');

     # send request
     my $res = $ua->request($req);

     my $page = $res->decoded_content;

List out the actions and we see just how wide a gulf this code has:

  1. Load LWP::UserAgent (which also loads HTTP::Request)
  2. Make a new LWP::UserAgent object.
  3. Set your HTTP user agent identification.
  4. Create a new HTTP::Request object.
  5. Prepare an HTTP GET request for the desired URL.
  6. Inform the server that we prefer HTML.
  7. Send the request on it's way to the server.
  8. Get the response back from the server.
  9. Decode the response according to the Content-Encoding.

And finally you have your page. Only the fifth action bares resemblance to the goal of "get a web page". The actions require intimate knowledge both of how the HTTP protocol works and how to work three different classes (one of which, HTTP::Response, is hidden). If the user doesn't understand all that, then it's all just so much mumbo-jumbo magic incantations which must be memorized by rote. It's the computing equivalent of setting the VCR clock.

At this point some of you might be thinking "that's not fair! There's so much more you can do with HTTP than just getting web pages! What about error handling? Authentication? Cookies? Multi-language support? Proxies?" You're right, LWP::Simple can't do anywhere near what LWP::UserAgent can do. But LWP::Simple can do one thing UserAgent can't: get a web page in just two steps.

There's an old cliche, "if it was hard to write, it should be hard to use", reflecting the programmer's own relationship with their creation. Of course the programmer thinks it's complicated, the act of getting a web page is a complicated process. They're focused on implementing the actions so that's what they tend to think about. The complexity becomes something to be proud of -- and why not they put a lot of work into it -- and that complexity leaks out into the interface so the user can appreciate just how much effort they went through.

But as far as the user is concerned it's dead simple: Here's a URL, give me back some HTML. They have no idea what goes on between, it could be tiny invisible winged ponies carrying little scrolls through the air for all they know, and they don't need to know. A good interface reflects the simplicity of the goal, not the complexity of the implementation.

The sad part of all this is in hiding complexity we allow the user to remain ignorant of all the effort put into making it simple. This leads to the converse fallacy at the user end, "if it was easy to use, it must have been easy to write" leading to a devaluing of all the programmer's effort. Alas, this is a paradox of good design. But look on the bright side, if the only time you hear about your software is when it breaks, take it as a compliment on your seemless interface design.


The second layer down, looking at the value of complexity underpinning simplicity, will be next.