sab39

... indistinguishable from magic
effing the ineffable since 1977

Categories

Show All

Recent Posts

Archives

API design, part 2

2/11/2005
When faced with the task of designing APIs for a problem that has a lot of inherent complexity, an essential first step is to design exactly how and where to hide that complexity. I was recently faced with the need to redesign an API that I'd created before I learnt this lesson, and I think I did a good job of it - the complexity is almost entirely hidden from users on both "producer" and "consumer" sides of the API.

However, in the process I've learnt another essential, if (in retrospect) obvious lesson: Just because you've figured out how to hide the complexity doesn't make the complexity go away. Implementing the code that actually does the hiding is still complex, probably even more so due to the need to provide the illusion of simplicity. Forget this at your peril!

 

A long and glorious tradition

2/11/2005

I'm referring, of course, to the long and glorious blogging tradition of posts which apologize for how long it's been since the last post. Since I'm perfectly sure nobody was actually on tenterhooks waiting for me to post something, there's nobody to actually apologize to, but sorry anyway.

API design

It's occurred to me that adding a function to an API based on the fact that many people want it can still result in making the API as a whole worse. Adding a new function seems like it should be harmless as long as you never call it, but it's not. Perhaps this is obvious: a good API should provide a coherent "mental model" of the system it's exposing, and adding a new function (even based on popular demand) can break that mental model, making it harder to get an understanding of how the API works.

This realization was crystallized for me by thinking about a couple of functions in the ASP.NET framework, one that exists because of implied popular demand, and one that is frequently requested but does not exist today.

The first is Request.Params. This property exists to provide superficial backward compatibility for people who know classic ASP, and it was presumably added to classic ASP itself based on popular demand (I can't imagine that any API designer really thought it was a good idea, but then again, I feel that way about quite a lot of things in classic ASP). Request.Params is a collection of name-value pairs that consists of an aggregation of (1) query-string parameters, (2) form post data, (3) server variables, and (4) cookies. I think there are actually even more than these, but those are the four I can remember. The problem with this API is that, well, people use it. And their code then gets completely confused if there's a form control that happens to have the name of a query-string parameter they were expecting. Yes, I've seen this happen. It's especially nasty because in ASP.NET you can rely on the framework to handle the form post data, so you almost never want to get it manually. If it's coming from a cookie or a server variable things are even worse because most programmers aren't even aware that these are included in Params, so they have no hope of debugging such problems.

There's CERTAINLY no case where a value should be treated equally regardless of whether it's a query-string parameter or a server variable. If Request.Params didn't exist, nothing would actually be any harder, because you can always get the data from the place it actually is, because you always know where it actually is. But because it does exist, naive people use it, and so things break.

(Request.Params also sometimes breaks in the presence of Server.Transfer, but that's not the reason you shouldn't use it. You shouldn't use it because it's never what you actually wanted in the first place.)

The function I was contemplating that doesn't exist is a way to determine which form control caused a post-back event to happen. This is a very natural thing to want to know, in fact there have been situations I've wanted it myself. I never understood why it wasn't present until someone else asked me how to get this information, and I asked why they needed it. The ASP.NET "mental model" is that the way you react to post-back events is by setting a handler on the event itself, for example button.Click. But because so many programmers' mental models are based on classic ASP and other technologies, they'd be inclined to write code like "if (PostBackControl == button) {}", instead, and break the mental model. While there are valid reasons you might want the information, writing code like that defeats the clean and elegant ASP.NET programming model. And unfortunately there's no way to provide the function for people who have a good reason without also providing it to the vast majority of people who want it for a bad reason. I'm forced to conclude that Microsoft made the right tradeoff by not providing it, although I do think it's a shame.

These Feet are Incorrect, Kate Walker

My absence has been in part due to my (re)discovery of Syberia, an adventure game (for Windows, no idea if any of the Wine-a-likes can run it) which puts you in the shoes of Kate Walker, a New York lawyer who has been sent to the picturesque fictional French village of Valedilene. Kate's ostensible mission is to obtain the signature of the elderly Anna Voralberg to enable a routine corporate takeover. However, the situation rapidly becomes more complex and Kate is left to unravel the mysteries of the Voralberg family. The story is split over two games, imaginatively titled "Syberia" and "Syberia II".

To call Syberia an incredible game is an understatement. The graphics are simply stunning: a combination of pre-rendered (sometimes animated) scenery, realtime 3D graphics and occasional cuts to pre-rendered video, which serve to enhance the story without detracting from the gameplay. The animation of the background scenery exemplifies the level of attention to detail paid to every aspect of the game: watch out for birds flying overhead during outdoor scenes, and keep an eye out for the always spectactular water effects. Between the graphics and the also wonderful music and sound effects, you're immersed in the world of the game in a way that even many movies struggle to achieve.

The gameplay is standard adventure-game fare: explore the environment, collect objects, interact with characters and solve puzzles. Syberia does an impressive job of providing puzzles that are challenging but logical, and fit well with the plot of the story. The common adventure game problem of how to force you to collect all the objects you're going to need later is deftly dealt with by turning it into both a key plot point and a running gag. The story is movie-like, too: at times infuriating, funny, moving, awe-inspiring and exhilarating. The plot unfolds slowly as puzzles are solved, but towards the end of the first game the pace begins to pick up.

The high point of the entire story is the end of the first game. After a particularly heinous puzzle involving a drink-mixing machine, I strongly suggest making sure you can devote an hour or two to finishing the game, because from that point on things happen at a breakneck pace. The creators of the game accomplish a truly amazing feat: a puzzle-solving adventure game that you finish on an adrenaline high!

I honestly can find absolutely nothing to criticize about the first game. Unfortunately the sequel, while still very good, did not quite achieve the same high standard and I found quite a lot of small nits to pick. The puzzles seemed less logical and required more of the "try every object you have against every object in the environment" approach that's the curse of the genre. I noticed several occurrences of another common genre problem, the "gosh, isn't it lucky I happened to have a rubber chicken" syndrome, where you have to pick up an object for no reason at all which will then just happen to be absolutely necessary to solve a puzzle much later in the game.

Apparently in response to criticism from players, the dialog engine was changed to avoid having the same conversation twice. At first this seemed like an improvement but after a while I came to the conclusion it was actually worse. The conversations with other characters are a key source of clues on how to solve the puzzles, but if you missed such a clue, you could never get it back. I suspect that this was part of the reason the puzzles seemed less logical: where in the first game if I missed a clue I'd be likely to eventually go back to the same person who would then tell me the clue again, in II I just had to do without and brute-force the puzzle.

As before the atmosphere and graphics of the second game were stunning. A dream-like sequence near the end was particularly impressive in the way it evoked emotion. I was disappointed by the plot and the ending, but only because my expectations had been raised so high by the first game - by any normal standard they were still very good.

All in all, I wholeheartedly recommend this game to anyone who likes solving puzzles and unraveling mysterious mysteries. My only caveat is to reset your expectations after the first game, because if you hold the second to that impossibly high standard you're likely to be disappointed.


 
RSS