Sunday, August 19, 2007

No simple answers

Which is the better deal: a 9-inch pizza for $9 or a 12-inch pizza for $14?

The gut-reaction answer is the 9-inch pizza. But that answer is incorrect.

The correct answer is the 12-inch pizza.

Assuming that the pizzas are round, the 9-inch pizza has an area of (almost) 64 square inches and a cost of $0.14 per square inch. The 12-inch pizza has an area of 113 square inches and a cost of $0.12 per square inch.

Why is it easy to pick the wrong answer? Because the two options were presented in terms of diameter, but the value is measured in area. To get the proper answer, you must convert the presented information into the form used for the calculation of value.

Or, in another view, the information presented was 'features' (a 9-inch pizza) and not 'benefits' (total area of pizza). Features are not (necessarily) benefits. Features are attributes of the product. Benefits are things that provide value. They are not the same. One can be converted to the other, provided you know the formula. For pizza, this is an easy conversion. For other items (like software) the conversion can be harder to define.

You knew I was going to get to software at some point, right?

When selecting software, it is easy to pick the one with the most features, on the assumption that it is better than the others. Or at least it will do everything that the other packages will do.

Or it is easy to pick the one with the lowest cost, on the assumption that the possible selections are roughly the same.

Both methods are easy.

And both methods rely on assumptions. Which means they may provide a good result, or may not, depending on the truth to the assumptions.

If you are selecting software, you must understand how the software will be used and how it provides value. You must know the benefits of the software, not merely the features. Only then can you select the best software for your needs.

Knowing how you will use the software is a hard task. It means understanding how people use the software today. It means predicting future use of the software.

It's much easier to simply pick the package with the lowest price. Or the most features.

But the the best solution may not have the lowest price may, nor the most features. Even when they coincide. As an example, consider air travel. If I had to travel from the East coast to the West coast, and were presented with two options: Continental for $299 and Southwest for $349, with departure and arrive times about the same, and Continental offering a free movie and meal on board, I would pick the Southwest flight. (Southwest offers no in-flight movie and provides at most peanuts.)

"Why?", I hear people ask. The Continental flight has a lower cost *and* more features (and other aspects are equal). The cost is better and the feature list is better. In this case isn't Continental the better choice?

The answer is that Southwest is the better choice, because it provides more value. Remember, features are not directly equivalent to value. The features that Continental provides have no value for me: I've eaten their in-flight meals and I find that a sandwich and piece of fruit from the local shop is better.

I've also seen their arrangements for in-flight movies and find it has negative value. Continental uses little flip-down screens that show the movie in multiple locations on there planes; there is no escape from it. A good movie is tolerable but a bad movie is torture. On average, the movie is a disincentive to use Continental. The extra $50 for the Southwest flight is worth it to *not* have a movie inflicted upon me.

There are no simple answers. It's all about the benefits, and identifying the meaningful benefits is work. The simple answers are often wrong.

Which means that the 9-inch pizza might be the better deal, when you are not that hungry.

Sunday, August 5, 2007

Two-cycle development

I have been working with software for a while now (more years than I care to mention) and I have found that I work in two different modes. The first mode is 'creating software' and the second is 'refining software'.

The 'creating' mode is when I add new features to a program. It could be at the very beginning of a development effort, when there are no features in a program, or it could be later, when the software is 'mature' (whatever that means).

The 'refining' mode is different. It is not adding features, but changing the software to perform the same feature set but with better internal design. This might mean replacing a sort routine with a better sort routine, refactoring a class for better organization, or changing a program for better division of labor. The important part in this phase is that no features change -- the program works just as before. The internals are different, the externals are the same.

These two modes of thought are, in my mind, part of the basic human psyche. At some times, our minds want to create. At other times, our minds want to re-arrange that which we have created. I consider both modes creative, although different types of creativity.

I think that two-cycle development is necessary. It's necessary because we don't get the design of the program right the first time. Oh, we get something that works, but that doesn't mean the design is right. Despite all of the planning, all of the analysis, and all of the reviews, we build software poorly. Refactoring allows us to examine our construct, think about the design, and figure out better ways to do the job.

Two-cycle development leverages our 'Monday morning quarterbacking' talents (even for our own work), and is possible with automated tests. The ability to re-arrange, to experiment, to try new things, is much easier with a complete set of tests. You need the tests to verify your experiment. Without automated tests, you must rely on manual tests, and manual tests are hard to perform. Since they are hard to perform, programmers tend to skip them. And when programmers skip the tests, they miss errors. After a few experiments in re-organizing code without tests (and a few introduced defects) programmers learn to avoid refactoring operations.

Without automated tests, development tends to avoid refactoring.

Without the refactoring, we use our initial, poor designs. While they work, the designs fail over time. And they often fail in slow ways. Failures occur not in spectacular collapses, but in slow decay. Any system built without refactoring gradually accumulates poor designs, and these poor designs increase the future work on the program. Over time, changes to the program take more time and effort, and the number of defects per change increases. The program gets tangled and hard to understand. Eventually, development grinds to a halt as each change introduces more defects than improvement.

To prevent this 'slow death by poor design', we must allow (and plan for) refactoring. And to refactor the program, we must do to things: allow developers time for refactoring and provide the tests to verify that the changes work. Without these, the program accumulates 'cruft' and becomes harder to maintain.