One of the maxims of Agile development — and especially Extreme Programming — is to write the simplest thing that can possibly work.
There are a few driving forces behind this motto. First, a core tenet of Agile development is to get testable, functional code up and running as soon as possible. If you don't spend a lot of time writing complex code, you can get it running sooner. And complex code is harder to maintain and thus more likely to have subtle bugs. Second, Agile assumes that requirements will change. This in part because requirements always change, but also because the mere act of showing a user a current piece of software will give them new ideas for the features that will really solve their problems. Users aren't good at articulating what they need until they see something "tangible" that isn't what they need. If you get a piece of code up sooner, you give your users the chance to steer the application in the direction they really want, not the direction they thought they wanted. And when they present this abrupt change in direction — as they inevitably will — you don't have to dismantle a complex system.
A close cousin of the "simplest thing" motto is "You're Not Gonna Need It." Experienced programmers tend to think in terms of frameworks they can build, good object-oriented principles, and so forth. But if you spend a lot of time building a nice, modular system with layers and abstraction, you may have wasted your time and effort if there will only ever be one module, one implementation of a given interface, or one class in a layer.
This is not to say that you should be sloppy, or churn out quick and dirty, copy-paste code. You are a professional and should act like one. Your "simplest thing" should be tested and it should be clean code. As the requirements change beyond what your "simplest thing" can do, you can then start to build layers on top of it, refactor to make it more modular, and so on.
All these arguments came back to me when I had to implement templatable text at work. I wanted text that a non-programmer could modify, but it needed to allow variable substitution.
Java's localization system allows this to some degree, except that its variables are numeric, not symbolic. So item zero is always the same, item one is always the same, etc. regardless of where they show up in the string. This makes sense for localization, but is fragile for more general templates. What if we wanted the text to use a different set of variables from one week to the next?
There are a number of general-purpose templating systems for Java: Probably the best-known is Velocity. As I started researching what it would take to implement it, however, the "simplest thing" motto came back to mind. Not that Velocity is difficult to implement, but we only needed to support five variables, and we didn't need any of the flow-of-control logic that Velocity offers.
So I wrote a very simple set of methods that allows me to do string replaces on the five symbolic variables I allowed. The (internal) user can write any text, using some or all of those variables once, twice, or whatever they want. And just in case we ever need to move to Velocity, I used the same variable naming syntax to define our variables.
The code is simple. It has noticeable restrictions (can't use undefined variables, can't define new ones), but in this case, it works exactly the way I need it to. It satisfied the business need, and truthfully, will probably never need to support more. When I needed to add an additional piece of templated text with the same variables, it took about 15 minutes including 10 minutes of testing.
Monday, September 14, 2009
Friday, September 4, 2009
Tutorial System, Part 2: Variadic Methods
In my last post, I mentioned a simple tutorial system that I wrote for my iPhone app: When I pass a key to the system, new users get a text box explaining the purpose of each screen, tap to close it, and then never see it again.
It worked well enough, though my testers have some usability comments that I need to address. But as I started working on version 1.1 of my app*, I realized that I had new needs. I had written, as I try to remember to do despite the temptation to overdesign, the simplest thing that could possibly work. That no longer fit the bill. (Writing the simplest thing that can possibly work assumes that design requirements will change. But since you can't predict those changes, even when you're the one creating them, you shouldn't waste time designing for requirements that don't yet exist. Address the problem in front of you, get functional code up and testable, and then refactor to simplify.)
I wanted the system to handle upgraders, so that upgraders would see text just about the new features, while new users would see a screen giving an overview of the page, including the new features. The key was that when a new user dismissed the tutorial box, s/he would never see the one for upgraders (because there would be no need).
I toyed with a few ideas, but I finally settled on one: Rather than passing a single key to the system, I'd pass a list of keys. The system would iterate through the keys until it found one the user hadn't seen, show the tutorial text for that key, register that the user had seen the key, and then register every subsequent key in the list.
In order to make this work, I used a variadic method, one with an indeterminate number arguments. It's been a while since I've been deep in a C-based language, so I had to refresh my memory about implementing them. (Curiously, the Objective-C bible, Programming in Objective-C, doesn't tell you how to implement them. Perhaps the new edition does.) They rely on the stdargs.h macros,
If you want to implement your own variadic method in Objective-C, here's the basic pattern (you don't need to import stdarg.h if you're importing the Foundation classes):
Here's the code in the context of my Tutorial system:
* I should note that 1.0 of my app is not yet out, despite being published on the App Store, because of some bureaucracy with my employer. Assuming it's going to get resolved. I've started on 1.1.
It worked well enough, though my testers have some usability comments that I need to address. But as I started working on version 1.1 of my app*, I realized that I had new needs. I had written, as I try to remember to do despite the temptation to overdesign, the simplest thing that could possibly work. That no longer fit the bill. (Writing the simplest thing that can possibly work assumes that design requirements will change. But since you can't predict those changes, even when you're the one creating them, you shouldn't waste time designing for requirements that don't yet exist. Address the problem in front of you, get functional code up and testable, and then refactor to simplify.)
I wanted the system to handle upgraders, so that upgraders would see text just about the new features, while new users would see a screen giving an overview of the page, including the new features. The key was that when a new user dismissed the tutorial box, s/he would never see the one for upgraders (because there would be no need).
I toyed with a few ideas, but I finally settled on one: Rather than passing a single key to the system, I'd pass a list of keys. The system would iterate through the keys until it found one the user hadn't seen, show the tutorial text for that key, register that the user had seen the key, and then register every subsequent key in the list.
In order to make this work, I used a variadic method, one with an indeterminate number arguments. It's been a while since I've been deep in a C-based language, so I had to refresh my memory about implementing them. (Curiously, the Objective-C bible, Programming in Objective-C, doesn't tell you how to implement them. Perhaps the new edition does.) They rely on the stdargs.h macros,
va_start
, va_arg
, and va_end
. In Objective-C, the convention is that the last item in the list be nil. For example, see NSArray's initWithObjects:
method.If you want to implement your own variadic method in Objective-C, here's the basic pattern (you don't need to import stdarg.h if you're importing the Foundation classes):
- (void) method: (id) arg, ... {
va_list argList; // special datatype for the argument list
va_start(argList,arg); // sets up the iteration
id curArg = arg;
while (curArg != nil) {
// do something
curArg = va_arg(argList,id); // pass the datatype you expect to find
}
va_end(argList);
}
Here's the code in the context of my Tutorial system:
+ (void) showTutorialForKey: (NSString *)key, ... {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
va_list argPtr;
va_start(argPtr,key);
NSString *curArg = key;
while (curArg != nil) {
if ([TutorialSystem userHasSeenKey:curArg]) {
curArg = va_arg(argPtr,id);
continue;
}
NSString *titleKey = [NSString stringWithFormat:@"%@ Title",curArg];
NSString* title =
[[NSBundle mainBundle] localizedStringForKey:titleKey value:@"" table:@"Tutorial"];
NSString* text =
[[NSBundle mainBundle] localizedStringForKey:curArg value:@"" table:@"Tutorial"];
TutorialController *controller =
[[TutorialController alloc] initWithTitle:title tutorialText:text];
[[[self appDelegate] window] addSubview:[controller view]];
// clear the user for all the rest of the args
while (curArg != nil) {
[TutorialSystem registerUserSeesKey:curArg];
curArg = va_arg(argPtr,id);
}
}
va_end(argPtr);
[pool release];
}
* I should note that 1.0 of my app is not yet out, despite being published on the App Store, because of some bureaucracy with my employer. Assuming it's going to get resolved. I've started on 1.1.
Subscribe to:
Posts (Atom)