The checkbox: The mating call of the loser

Raymond Chen

(Cultural note: The phrase the mating call of the loser is a term of derision. I used it here to create a more provocative headline even though it’s stronger than I really intended, but good writing is bold.)

When given a choice between two architectures, some people say that you should give users a checkbox to select which one should be used. That is the ultimate cowardly answer. You can’t decide between two fundamentally different approaches, and instead of picking one, you say “Let’s do both!”, thereby creating triple, perhaps quadruple the work compared to just choosing one or the other.

It’s like you’re remodeling a book library and somebody asks you, “Should we use Dewey Decimal or Library of Congress?” Your answer, “Let’s do both and let the user choose!”

Imagine if there were a checkbox somewhere in the Control Panel that let you specify how Windows XP-styled controls were implemented. Your choices are either to require applications to link to a new UxCtrl.DLL (let’s call this Method A) or to link to COMCTL32.DLL with a custom manifest (let’s call this Method B). Well, it means that every component that wanted styled common controls would have to come in two versions, one that linked to UxCtrl and used the new class names in its dialog boxes and calls to CreateWindow, and one that used a manifest and continued to use the class names under their old names.

#ifdef USE_METHODA
 hwnd = CreateWindow(TEXT(“UxButton”), …);
#else
 hwnd = CreateWindow(TEXT(“Button”), …);
#endif

DLG_WHATEVER DIALOG 36, 44, 230, 94 STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_SHELLFONT CAPTION “Whatever” BEGIN #ifdef USE_METHODA CONTROL “Whatever”,IDC_WHATEVER,”UxButton”, BS_AUTOCHECKBOX | WS_TABSTOP, 14,101,108,9 #else CONTROL “Whatever”,IDC_WHATEVER,”Button”, BS_AUTOCHECKBOX | WS_TABSTOP, 14,101,108,9 #endif …

At run time, every program would have to check this global setting and spawn off either the “Method A” binary or the “Method B” binary.

Now you might try to pack this all into a single binary with something like this:

if (GetSystemMetrics(SM_USEMANIFESTFORSTYLEDCONTROLS)) {
 hwnd = CreateWindow(TEXT(“Button”), …);
} else {
 hwnd = CreateWindow(TEXT(“UxButton”), …);
}
…

if (GetSystemMetrics(SM_USEMANIFESTFORSTYLEDCONTROLS)) { DialogBox(hInstance, MAKEINTRESOURCE(DLG_TEMPLATE_WITH_OLDNAME_CONTROLS), … } else { DialogBox(hInstance, MAKEINTRESOURCE(DLG_TEMPLATE_WITH_UXCONTROLS), … }

But it’s not actually that simple because a lot of decisions take place even before your program starts running. For example, if your program specifies a load-time link to UXCTRL.DLL, then that DLL will get loaded before your program even runs, even if the system switch is set to use Method B. A single-binary program that tries to choose between the two methods at runtime will have to do some activation context juggling and delay-loading. Hardly a slam dunk.

Okay, so now you have two versions of every program. And you also have to decide what should happen if somebody writes and ships a program that uses Method A exclusively, even when the system switch is set to Method B. Does everything still work within that program as if Method A were the system setting, while the rest of the system uses Method B? (If you go this route, then you’ve completely undermined the point of Method B. The whole point of Method B is to allow programs that rely on specific class names to continue working, but this rogue Method A program is running around using the wrong class names!)

Now the entire operating system and application compatibility work needs to be done with the checkbox set both to Method A and to Method B, because the compatibility impact of each of the methods is quite different. Okay, that’s double the work. Where is triple and quadruple?

Well, the two different versions of the program need to be kept in sync, since you want them to behave identically. This can of course be managed with judicious use of #ifdefs or runtime branches. But you have to remember both ways of doing things and be mindful of the two method each time you modify the program. Somebody else might come in and “fix a little bug” or “add a little feature” to your program, unaware of how your program manages the shuffle of Method A versus Method B. The mental effort necessary to remember two different ways of doing the same thing plus having to expend that effort to correct mistakes in the code, that’s the triple.

The quadruple? I’m not sure, maybe the ongoing fragility of such a scheme, especially one that, at the end of the day, is a choice between two things that have no real impact on the typical end user.

Engineering is about making tradeoffs. If you refuse to make the choice, then you’re taking the cowardly route and ultimately are creating more work for your team. Instead of solving problems, you’re creating them.

All because you’re too chicken to make a hard decision.

0 comments

Discussion is closed.

Feedback usabilla icon