I realized something unpleasant last week: it's highly unlikely that David Hasselhoff will ever get another prime-time show. Shortly afterwards, I realized something else that was even more unpleasant: the design reviews we were doing at work just weren't helping.
Most people would agree that a design review is helpful. Not only does it allow the designer to bounce ideas off of a group and perhaps find some improvements in his original concepts, but it allows the group to learn something about a part of the program with which they may not be familiar. Both of those are good things. However, when you try to implement design reviews in real life, you may find that an actual review is far less helpful than one originally conceived. What's going on here?
Usually, a design review is done after you've designed something. This is bad for two reasons. First, how is the review supposed to be collaborative if the design is already done? The best time to collaborate on something is at the beginning when changes are cheap, not at the end when changes become costly.
Second, if you're anything like me (both impatient and brutally handsome), you code as you design. I can think about the design as much as I want, but the best way for me to see its limitations is to interact with it. By the time the design review rolls around, it's highly likely that not only is the design done, but the code is done also. Thus, any changes will effect the design and the code, which is probably already working. Why change something that already works? How many times can you change something for the sake of design purity?
I speak from experience here; our team originally set up our design reviews just as I previously described, and they weren't working as well as we wanted. As any engineer can tell you, a feedback loop is useless if the loop is too long. Taking that to heart, we shifted the feedback from the end of the design process to the beginning. Now, rather than having a lone developer slaving away in her office on a design (which was never mandated, but how it turned out often), whenever one of us is designing something, we have to get together with at least one other dev to discuss our design ideas. This other person isn't a road-block, just a check to make sure that we're not missing anything big. There has to be a consensus before you can proceed. This allows us to find the big mistakes at the beginning, not at the end.
Like any other sane individual, I'm a big believer that a process should help, not hurt. Before, we had a clear case of a bad process, one that kept us from correcting mistakes unless the mistakes were mind-boggingly large. Well, maybe the mistake wasn't huge right then, but after a few years of maintenance, it's easy to see how a minor design oddity could become the size of a brontosaurus, smashing cubicles and builds left and right. The situation is still not perfect, but it's a little better. That's enough for me.
I like to take note whenever I make a mistake, so I can keep from making that mistake in the future. Sometimes, these lessons are valuable (don't eat thermometers), and other times, they're a little less valuable (Jerry Van Dyke is one heck of an entertainer). Just recently, I received one of those more-valuable lessons, courtesy of our automated build process. The lesson: never assume you know why the build is failing.
We needed a major database schema change made. At the same time the data was changing, our code was changing to reflect the new schema. The first time we checked that code in and ran the build, we got a SqlException. "Good," we yelled, "that's probably supposed to happen because the schema change hasn't been made yet!"
Time passed, the build continued to fail, and we continued not to worry about it; after all, the schema change hadn't been made yet. Then, the schema change went live. I ran the build, expecting to see a comforting green light. You can probably guess where this is going. The build failed again, with the same SqlException. All at once, everyone went back and checked the error message on the build. Yeah, it was definitely a SqlException, but if you dug a little bit, the error message had nothing to do with schema. We had a malformed query. Oops.
Sometimes, you expect a build to fail because of schema changes and whatnot; you can't get around these. However, if you expect it to fail, it does so, and then you don't verify the reason it failed, then you're setting yourself up to wear the dunce cap. You lose the best part of continuous integration then, in that you no longer care if your changes are valid. It could fail for any reason, and you could still lean back in your chair and declare, "I totally expected that."
No matter how certain we are, we must validate our assumptions, especially when they're that easy to check. If not, like the old saying goes, we allow the word "assume" to make an ass of you and me. Or maybe we're allowing the made-up, but similarly defined word "assus" to make an ass of us. Whatever the case, it's not good.
I am a big fan of agility. That doesn't mean I appreciate the fluid movements of the puma and the billy goat, but that I appreciate the goals of agile, iterative approaches to development. I'm a fan, but not a slavish admirer; I think you need to understand iterations before you can apply them. To understand iterations, you only need to answer one question: How big is your appetite? Let's look at an analogy.
One day, noon rolls around and you discover you're hungry. You go into kitchen, ready the bread and bologna, and announce to the world, "It's sandwich makin' time!"After all of the cool stuff you've been reading on agile methods, you decide to apply some here. You tear off two square centimeters of bread and a square centimeter of bologna. With them, you construct a very, very tiny sandwich. You put it in your mouth and say, "Mmmmm, now that passes the unit test!" But that tiny sandwich isn't enough; you need several more. You repeat the process several dozen times until your stomach finally says, "Back off, Boudreaux." Then, at the end, you analyze what you've done. You got to tear off 45 little pieces of bologna, so you really, really perfected that part. No matter how good you got, though, it still took a while to tear off all of the little bologna chunks and put them between your little bread chunks.
The next day comes, and you decide to take a different bent towards the problem. Iteration is out the window; you fart in its general direction. Instead, you'll streamline and do it all in fell swoop. As you ready your materals, you have another great thought. You think, "Hey, I eat sandwiches every day. Since I'm making myself one today, why not make several more?" Emboldened, you set to work and a twenty minutes later, you've created 15 sandwiches.
The first one is great. The next day, you eat the second and you're still happy. After a week, however, you find yourself getting a little tired of the bologna. The bread is going stale and the meat is sporting a few fuzzy green spots. You want something else, but you feel obligated to eat your way through your bologna stockpile.
People do approach software this way, though. I've talked to people whose iterations consisted of literally a single feature. The difference between releases was so marginal, there was hardly a point in even releasing it; it was like making a sandwich a bite at a time. On the contrary, we've all been involved in projects where we did nothing but stockpile sandwiches in the hopes that someday, someone would want to eat them.
The point of an iteration is, I think, to satisfy without overwhelming. An iteration aims to please end-users without frustrating them. Applying that to the sandwich world, it means you make only as many sandwiches as you plan to eat for that meal. Not only that, but you make only the kinds of sandwiches you wish to eat; the menu isn't set until lunch time, in other words.
The scope of an iteration will always be nebulous; you can't whip out a formula or chart it in Excel. Ultimately, the only way is to go to the user and ask them the size of their appetite. Deliver no more and no less. Then, when they're ready for more, you deliver more. In that way, you define your iterations. If that sounds nutty, well, you can always go back to making your sandwiches one bite at a time.