usable in any place a human can be used

20091103

i can't stop you from being stupid

computer-stupid

I've been working on a skunkworks side project that is nearing release, I'm down to the last 5%, which is the most difficult part. Suffice it to say that this project is a library meant to be used by other developers (and myself) to develop neat and nifty things. One of the most complicated things is trying to define an API that makes doing the right thing easy and doing the wrong thing really hard or impossible. The problem is that the as I pressed on I realized that almost half my code was checking for some stupid thing and protecting against it, attempting to build up state machines and syntax trees in the hope of reporting to you that you had done something stupid, and then it dawned on me, I can't stop you from being stupid.


The other thing that dawned on me is that I shouldn't try to, there are already built in errors that will point out what you did wrong, I don't need to add another layer on top. So I came up with some simple guidelines, applied it to my project, and watched unnecessary code melt away.



  • Pick sane defaults

  • Assume the developer knows what they are doing

  • Make it easy to do the right things

  • Make it hard to do the wrong things


The beautiful part of all of it is that my code became easier to read and understand, which means when there is a problem and you need to drop into my code (since it is open source), you will be able to understand what's going on. Let's take a look at what I mean with a contrived example, an HTML building library with 4 functions for clarity.


[php]
class HTML {
static function open_html() {
echo "<html>";
}

static function open_body() {
echo "<body>";
}

static function close_body() {
echo "</body>";
}

static function close_html() {
echo "</html>";
}
}
[/php]

The way I was programming did a bunch of handholding and sanity checks, which were nice but unnecessary. The class became bloated with so much state and sanity checking that it was becoming unwieldy, let's take a look at an overly protective incarnation of the above code.


[php]
class HTML {
private static $html_open = false;
private static $body_open = false;

static function open_html() {
if(self::$html_open) {
self::handle_error("html is already open");
}
echo "<html>";
self::$html_open = true;
}

static function open_body() {
if(self::$body_open) {
self::handle_error("body is already open");
} else if (!self::$html_open) {
self::handle_error("body must be contained in html");
}
echo "<body>";
self::$body_open = true;
}

static function close_body() {
if(!self::$body_open) {
self::handle_error("cannot close unopened body");
} else if(!self::$html_open) {
self::handle_error("body must be contained in html");
}
echo "</body>";
self::$body_open = false;
}

static function close_html() {
if(self::$body_open) {
self::handle_error("cannot close html, unclosed body exists");
} else if(!self::$html_open) {
self::handle_error("cannot close unopened html");
}
echo "</html>";
self::$html_open = false;
}

static function handle_error($message) {
//What is the correct behavior, should we attempt to fix the error, report it, who knows.
//We'll just stop execution
die($message);
}
}
[/php]

Those sure are some nice error messages and it keeps you from creating malformed html, but what's the point of it all. The browser is more than happy to tell you that your markup is invalid, or run the output through a lint checker or W3C validator, why should this class have some buggy half-implemented validator inside of it. The answer is, it shouldn't.


If you write stupid code you should get stupid results, Garbage In - Garbage Out. Library code shouldn't hold your hand making absolutely sure that you never make a mistake, what's the point of it. If you write the following code


[php]
HTML::open_body();
HTML::close_html();
HTML::open_html();
[/php]

You are clearly missing something about how markup works, it's not the library's job to hold your hand and guide you through this crisis, the browser will slap you in the face and you will have to learn something. Now there is a caveat, here we are generating HTML, there are great utilities for finding errors in HTML, so we don't need to reinvent the wheel. If you are writing a library or application code where if something goes wrong the error displayed is fine, don't reinvent the wheel. If the error is unacceptable you may need to do some error reporting.


At the end of the day you can't keep the user of your creation from being stupid, they are going to do stupid things and stub their toes and curse your name and you need to make sure that they had to go far off the beaten trail to do so, overlooking obvious better ways to do it, so that it's their fault and not yours. Make it easy to do the right thing and hard to do the wrong thing, but if the user wants to open the body tag before the html tag, let them, they have to learn sooner or later.

1 comment: