Monday, January 29, 2007

the accidental user

People won't use your software how you think.

Sometimes you lovingly plan, spec, prototype, usability test and construct your software to do something - something so wonderful that your users will dance ecstatically through the streets and erect statues in your honor. And sure enough some people are using your program - in ways you absolutely didn't expect.

I wrote the system configuration utility for Windows as a vendor (you can see it on an XP or 2003 machine by choosing "run" from the start menu and entering "msconfig"). It was designed to be a support tool for assisted support scenarios, when you're talking with the help desk and they direct you to run the tool to disable things and work out the problem with the system. It dealt with services, edited the boot.ini file plus a bunch of other things. It was designed to work this way by a series of program managers and usability experts.

So of course everyone just uses it to turn off their startup programs.

If we were designing a startup program, um, turner...offer, we would have done it differently. It's not a particularly elegant tool for the job. On reboot it announces itself and tries to run again, something that makes complete sense for the original scenario but a pain if you're just turning off startup items (fortunately it's easy to tell it to stop). But since it is built into the OS it's easy for users to instruct others on how to run it.

This kind of re-purposing happens all the time in pharmaceuticals: side effects discovered during clinical trials turn into the main use of the drug. It's a happy accident when users find your software's benevolent side effects but there are some steps you can take to make this more likely:

Extensibility

First, you can build in extensibility. This can be through something like a plug-in architecture or a scripting language (a few of the requirements for Steve Yegge's living software). Let your enthusiastic users mold your software to do new things. It's more work to build in extensibility but for significant systems it's worth it. Photoshop wouldn't be the ubiquitousness tool it is today without it.

Listen to Your Users

There are some obvious ways of doing this - running forums, e-mail links in your program to send comments, etc. Be sure you monitor those channels. You can also build some of this into your application, automatically collecting data on how it's being used. Be careful with this, though, and make sure your users know what sort of information you're collecting and what it's used for. Your users need to be able to turn this off (and you should never turn it on without asking them).

Finally, your bug reports can give you valuable information on what areas of the program are being used the most. And whenever you close a bug "by design", ask yourself why the user would have made the assumption he or she did. Should your application work how they expected?

Release Often

It doesn't go any good to listen to your users if you can't change the software. If you have a web app this is easy (just post the new code). It's harder for traditional applications but you can make it a little easier. The more thorough and automatic your tests are the better you can sleep at night during fast release cycles (you've certainly heard this before). Most modern software needs to be able to check on the net for new versions and inform the user (again, get permission from the user to make this check - it is not cool to make random net accesses your users don't expect).

Some day your software might adapt itself...it might be smart enough to see how people are using it and change the underlying architecture and code to work that way. Better than it was before. Better, stronger, faster.

But we probably ought to fix the buffer overflows first...

Wednesday, January 24, 2007

you don't bury survivors

(Overheard at a recent interview:)

So, Jeremy, tell me a little about yourself.


Because they'll roll and they can't fall down the hole. And because manholes are round.

Um, okay, how about your most recent project?

Turn on two switches, wait a while and turn one off. If the bulb is on, it's the one left on. If the bulb is off and warm, it was the one you switched off. Otherwise it's the one you didn't touch.

How big was the team you were on?


You don't bury survivors.

Uh, what was your contribution to the team?

Light both ends of fuse one and one end of fuse two. When fuse one burns out, light the other end of fuse two.

How would you deal with adding a feature to a late project?

A and B cross, A comes back, C and D cross, B comes back, A and B cross.

How would you handle a conflict between coworkers?

They're seven and a half degrees apart.

What programming languages are you most comfortable with?

Sometimes I'm too much of a perfectionist and I expect too much from my teammates.

You're hired.

Sunday, January 21, 2007

namm 2007

Most years I meet up with some friends (scattered across the country) and we head to southern California for the NAMM convention. The National Association of Music Merchants is held each year in the Anaheim convention center and brings manufacturers of musical instruments and equipment from around the world to show their newest inventions. Some of which are pretty weird.

The crowd at the show is one of the more eclectic you'll find: Chinese businessmen in suits, manufacturers in logoed polo shirts, music store owners, rock stars (current, past and wannabe) and their inappropriately dressed girlfriends. We always have fun trying to spot musicians ("Hey, there's Neal Scon from Journey. And Michael Anthony from Van Halen") and working at getting into the manufacturer's parties.

The level of talent around the show is always mind boggling and reminds me what a total hack I am. In the course of a couple of hours I can go from Doyle Dykes ripping it up in the Taylor guitar booth, to Keb Mo at a manufacturer's party. Then walk out of the party to find Johnny Hiland just smoking on a stage in the hotel lobby. Even the guys playing demos are fantastic (well, almost all of them).

This year was kind of lacking in stupid new gear. Sure, there were the usual ridiculous guitars and insanely priced acoustics with more abalone and mother of pearl than wood. But there was nothing to match the toilet seat guitar, or the backwards guitar, or the first time I saw the flavored sax reeds.

Well, there's always next year...

Wednesday, January 17, 2007

it might seem like a good idea...

To take your laptop and phone with you on vacation. But it really isn't.

Trust me on this.

Monday, January 15, 2007

codezilla

Consider how an engineer designs a bridge. There's a lot of analysis and math, plus there's a big game of "what's the worst thing that could happen":

"What if there were a fire here, fueled by the natural gas line, how hot could it get? What would it melt? What if a big truck smacked into this support? (Engineers LOVE using technical terms like "smacked into" or "walloped".) How many beams could we lose here and still have the bridge stand? What would happen to the bridge in an earthquake? What if it were a deep earthquake instead of a shallow one? Where's the worst place a bomb could be planted? How fast can the wind get here, are there any harmonics we need to worry about? What about Godzilla? Can we conclusively rule out Godzilla?"

Have you ruled out Godzilla for your code?

It's about designing for the unexpected. Making code that not only works now, but keeps working when the conditions change (or at least calls definitive attention to itself). For an example, let's come up with a simple-minded method to add a string to the start of a linked list:

   1: void AddString(char * s)
2: {
3: Node * p = new Node(s);
4: p->next = this->head;
5: this->head = p;
6: }
This sample represents level 0 of robustness - what you can crank out without thinking. At first glance this might seem to be enough, especially if you're the only one calling this function. You know you'll only pass in good parameters. This breeds a false sense of security, though...someone else might eventually call this code, or you might call it nine months from now when you've forgotten all the assumptions you made.

The next level represents some basic debug-build robustness. (For those unfamiliar with the idiom, an assert is a function that fires when its expression is false, stopping the program in the debugger so the programmer can figure out what went wrong. Typically it's only checked on the slower, debug builds used for testing - not the fast release builds that are shipped to customers.)
   7: void AddString(char * s)
8: {
9: Assert(s != null && s[0] != '\0'); // don't allow null or empty strings
10: Node * p = new Node(s);
11: Assert(p != null); // the allocation might have failed
12: p->next = this->head;
13: this->head = p;
14: }
This is great for catching bugs during development but it isn't much use to the end user running your software she just downloaded. For that, you need some checks that are done for release builds:
  15: void AddString(char * s)
16: {
17: if (s == null || s[0] == '\0') // don't allow null or empty strings
18: {
19: // Error: do something.
20: }
21:
22: Node * p = new Node(s);
23:
24: if (p == null) // the allocation might have failed
25: {
26: // Maybe you weren’t listening: DO something.
27: }
28:
29: p->next = this->head;
30: this->head = p;
31: }
(It isn’t easy to figure out what to do when you detect an error. It's a complicated subject that deserves an article of its own.) Sometimes you'll see this type of code with the asserts still included, just to break to the debugger if "impossible" situations are encountered.

How can you make it even more robust? You can introduce the idea of pre and post conditions for your functions. These are the contracts you're agreeing to with your function and you can include code to enforce these contracts (back to asserts to keep things short):
  32: void AddString(char * s)
33: {
34: Assert(IsValidListWithNoCycles(this->head)); // we're assuming this is true when the function starts
35: int startSize = GetListLength(this->head);
36:
37: Node * p = new Node(s);
38: p->next = this->head;
39: this->head = p;
40:
41: Assert(IsValidListWithNoCycles(this->head)); // this better still be true
42: Assert(GetListLength(this->head) + 1 == startSize); // and we can check that we added something
43: }
What more can be done? How about adding another thread to watchdog the data structure?
  44: void ListWatcherThreadFunction(Container * c)
45: {
46: while (1) // or check for an exit flag
47: {
48: Assert(IsValidListWithNoCycles(c->head));
49: Sleep(); // or wait or whatever
50: }
51: }
This might very well be overkill, but it might be warranted in some situations. Are there any other ideas for making this more robust?

Of course there are some drawbacks to adding this robustness. You add complexity and you add more lines of code to maintain (and debug). You might introduce performance problems, but those really should be measured and any really slow sections of code can be made "debug build only".

When you do a code review with your team (you do have code reviews, right?) what level of robustness do you expect to see? Has your team ever worked out what's appropriate for the project? Maybe you should call a truce in the vicious battles over bracket placement (let me settle the arguments for you: my way is the right way) and devote some time to debating what kind of robustness should be designed into the product.

And whatever you do, do NOT forget the hazards of Godzilla.

Saturday, January 13, 2007

welcome to code slate (or is that code's late?)

I kind of like the ambiguity in the the blog name (and domain). If you read it as "code slate", it's about writing software on a clean slate. If you read it as "code's late", well, that's also about writing software.

So here's the first post - the post which nobody will read since the blog is brand spanking sparkly new. Well, somebody might (hi Mom!). Along with the usual funny pictures and random thoughts, I'm planning on writing about how to make excellent software.

What's excellent software? Well, it's small, fast, intuitive, has an easy user interface, is robust, has beautiful code. It's trustworthy, helpful, friendly, courteous, obedient and cheerful. Yes, excellent software is like a boy scout, but generally with fewer fart jokes. I'll be writing about coding and user interface design (for which I have a lot of experience). I'll also sometimes write about marketing and the state of the software industry (for which I have, um, a lot of opinions).

A brief bit about me: I went to school at the University of Washington where I majored in computer science (back when it was still in the crappy building). Since then I've written a lot of software - I was the director of product development for a medical software company, and I've been a consultant since around 1995 (including writing msinfo and msconfig as a vendor for Microsoft).

By the way, that picture of me over there? It's the best one I could find but I seem to be wearing a tie in it. Which means either somebody got married or somebody died.