Archive for December, 2007

User Interaction as Marketing

Monday, December 17th, 2007

Successful marketing makes a product more desirable. User interaction design makes software more desirable. So is user interaction design successful marketing for software? I think so.

With so many folks talking about the iPhone right now, it surprises me how few people remark on the effect of its lovely interaction design. Notice the difference between the advertisements for the iPhone and every other phone on the market. Other phones show people talking on the phone, snapping pictures of each other, dancing around, getting into kung fu battles with phones as weapons, everything except what the iPhone ads show you: how you use the phone. Think about that. They sell the iPhone by showing you thirty seconds of someone using it, assuming that this will make people want it … which it does, because people recognize that using a product with good interaction design feels good.

- Jonathan Korman, Intuition, pleasure, and gestures

If we intend to sell our software to the masses, we need to adopt the iPhone marketing model. Whether or not you agree, most people find the iPhone ads desirable. My friend's 5-year-old son will come running into the room to watch a commercial when he hears the distinctive iPhone music. I will stop my TiVo to watch an iPhone or iPod Touch ad. And I own an iPod Touch! The interface is so pleasurable that we want to watch people use it.

But even companies that don't have the money to run TV spots should aspire to the standard that Apple has set. Software sells by word-of-mouth. Every time a user fires up your software, they should be seeing an ad.

I'm not talking about ad-supported software. I'm talking about the user having such a great experience that they want to tell people about it. I'm talking about a user interface so fun that people want to watch it on YouTube.

The iPhone is sorely lacking in features. The iPod Touch has extremely limited capacity for the price and crashes fairly often. But the icons fly in when you hit the home button. And the album cover flips and rotates when you turn it. These are the things that you see in the commercials. These things are fun. These are the things that sell.

Behavior vs State

Tuesday, December 11th, 2007

When discussing topics such as definitiveness or dependency, I'll use the word "behavior" when others might say "state". The difference might seem academic, but it has some practical ramifications that become apparent later.

The behavior of an object is what you can observe. It can be measured using the object's public interface alone. The interface is a contract that constrains an object's behavior, and that contract doesn't allow you to see anything else. This is the object-oriented concept of information hiding.

The state of an object is what it contains. You need to pierce the public interface or use a debugger to observe an object's state. A loose contract (a.k.a. a "leaky abstraction") might let some of that state show through. The object uses its own state to implement its behavior. This is the object-oriented concept of encapsulation.

So the behavior of an object is implemented in terms of state. But behavior can be described without invoking state at all. I can say that the behavior of a business object is dependent upon the behavior of a row in a database. There may be caching going on, and that cache may have state that can change. But that doesn't make it dynamic. A properly implemented cache is still dependent, because it behaves that way.

When considering the public interface of an object, behavior is all that matters. So when I want to say something about an object and its interaction with other objects, I'll limit myself to talking about behavior. I'll ignore state until it's time to code.

The behavior of numbers
Consider the number 3. I can represent this number in any of a number of ways. I can draw three vertical lines. I can hold up three fingers. I can draw the numeral 3. Or I can store the bits 00000011. These are different states that represent the object 3, depending upon its implementation.

Mathematicians have defined integers based entirely on their behavior. They say that there exists an integer called zero. They also say that if you increment or decrement any integer, you will get another integer. This is a sufficient definition, and it has nothing to do with representation.

So you can define the number 3 as the integer that you get when you increment zero, increment the result, and increment again. It doesn't matter how you store the state "3", a correct implementation will exhibit this behavior.

If we use vertical lines, you increment by drawing one more. If we use fingers, you hold up one more. If we use binary, you flip bits from left to right, stopping at the first zero-to-one transition. These are all implementation details based on state. But the behavior is always the same.

The effect of describing behavior
If I limit myself to talking about behavior, then I get to do a few good things. First, I get to say what I mean without a bunch of qualifications. I can say that the business object depends upon the database row without having to describe the cache that sits in between. I can say that history grows indefinitely, even though a program that actually did that would eventually crash.

Second, I get to procrastinate. I can put decisions off until I have enough information to make them. I can implement an object's behavior one way, then measure the system to see if I need to change it.

Third, I get to prove things. There is a reason that mathematicians define integers based on behavior. It's hard to prove things about state. Formal proofs of algorithms is notoriously difficult. But proofs based on behavior are trivial by comparison.

When I explore some techniques for historic modeling in upcoming posts, I'll be talking about the behavior of objects. When getting down to the implementation details, I'll start talking about state. It's the difference between the two that makes these techniques so useful.

Historic Modeling

Wednesday, December 5th, 2007

There is one pattern for using definitive behavior that I failed to mention in the previous post. This is perhaps the most important of them all, so it deserves special treatment.

Historic modeling is quite simply storing a history of actions, not the current state of a system. The current state is mutable, whereas history is fixed. Once something has been done, it cannot be undone, and it cannot be changed. A history is an ever growing log of permanent records.

One example of historic modeling from the real world is a chess game. The position of the board changes regularly during the course of a game. The position is dynamic. Yet the record of moves does not change. Once a move is made, it cannot be taken back or changed.

Another example is a checkbook register. The current balance can change over time, but the history of checks, once written, never changes.

Advantages of historic modeling
History is sufficient. Given a history of a system, it is trivial to go back to any point in time to construct it's state. If you have a record of chess moves, you just make each move in turn to produce the board position at every point in the game. If you have a ledger of checks, you can just add up the amounts including the opening balance up to a certain point in time. The current state doesn't contain enough information to reconstruct the history, but the history is sufficient to reconstruct the current state.

History is atomic. Each action in the history has happened. Actions not yet in the history have not. There is no middle ground. This is why banks keep ledgers of account transfers, rather than trying to decrement one account balance and increment another. Balance, being state, is not atomic.

History is consistent. Two people comparing notes can easily disagree on the current state of a system. To come to an agreement, they must compare histories. That's why the bank sends you a list of transactions at the end of each month, rather than just your account balance. That's the only way to reconcile your version of the truth with your bank's. Eventually, all parties will come to agree on the same version of history.

History is transparent. Logging and auditing systems exist to help us trace anomalies to their source. Without them, all we could do is look at the current state and wonder if it is correct. It is relatively easy to hide money in a series of accounts; but if you must produce an accurate history of transactions, each one can be called into question.

History as definitive behavior
So what does historic modeling have to do with definitive behavior? After all, history is always growing, while definitive behavior is something that doesn't change during the life of an object.

While the history of a given system changes, each event in history is constant. Once it is added to history, it cannot be changed. The behavior of the event is definitive, even if the history of events is dynamic.

The effect of an event might change over time. For example, you might think you have enough in your account to write that check, only to learn later that your spouse made a withdrawal beforehand. In your original history, that check left a positive balance in the account. But after learning about other events, you discover that the check left you overdrawn. Still, that doesn't change the fact that the check was written for a certain amount.

History in practice
By the above discussion, a historic model is a database that allows inserts, but no updates or deletes. Every time you need to make a change, you insert the appropriate type of record. To determine the current state of the system, you need to query the aggregate of history.

This is obviously impractical. Such a database would grow without bounds. The aggregate queries would get slower over time and eventually make the system unusable. We need one more observation to make it all work.

A system is historic if it appears to be so, whether it actually works as advertised. This observation allows for optimizations like caching, archiving, and pruning. As long as there is some level of abstraction at which you cannot see that the optimization is taking place (without using a stopwatch), then you can still claim to have a historic model. Future posts will discuss these optimizations in detail.