Design by committee



  • Working on a project for a company in Redmond (cough), a debate started. Part of a library I was building to automate UI tasks consisted of several functions designed to work with controls. FindControl, FocusControl, ClickControl, InputControl, that sort of thing. And then the feeping creaturitis set in.

    At first, the community (internal developers using the code) wanted a method for DoubleClickControl (no, it wasn't "ClickControl(); ClickControl();" - it was "if(ClickControl()) ClickControl();", which was somehow an improvement). That was simple enough. Then they wanted a more readable version of the *Control methods: specifically, although one of the arguments to the method was the type of control desired, they wanted FindButton and FindTextbox and FindLabel (after some arguing, they agreed this should be an alias for FindStatic) and so on. Essentially, it was too much trouble to type "FindControl(TYPE_BUTTON)" when you could type "FindButton()".

    I've oversimplified, of course. FindControl() took seven parameters. Only one was required - a string identifying the desired control - and the rest offered fine-grained control over where it looked and whether to search for children of a given window and the area of the screen where it was likely to be. Again, these aren't original design, which was just the one required string parameter. These are community suggestions based on making the library useful to more people in more cases.

    And then someone complained that the parameters were in the "wrong" order. So they demanded the parameters go in the "right" order. Unfortunately, nobody could agree on what the "right" order was. And then they noticed that all the parameters were different types, so the compiler didn't care, and we could generate multiple versions handling all the desired orders. Indeed, someone observed, we could handle every possible combination of desired orders - in addition to every possible combination of optional parameters.

    So here's how the scope creep manifested itself. From about six methods, we ballooned to handle a dozen types of specific control, so now there were 78 methods. Each method took from one to six parameters (seven for the six base methods), arranged in every possible order. That's slightly more than 6000 versions of each method, for a grand total of just under half a million method signatures.

    I didn't write this by hand, of course. I wrote a small C# program that generated everything and wrote all the code for me. But somewhere out there, checked into source control, there is a derived C# class containing half a million methods that all stub down to a half dozen methods in the base class. Of course, you never have to be bothered remembering the order of the parameters, which - on balance - may be a net win. But I'm betting a few jaws drop when they write the name of the method and see Intellisense pop "FindButton(string controlName) + 6000 overloads".

    And if you work there and you've seen it, yes, I WROTE THAT CODE. Or, more correctly, I wrote the code that wrote that code. I've owned up to it. My conscience is clean.



  • @CDarklock said:

    I've owned up to it. My conscience is clean.

    Doesn't this only work for catholics? And even then you have to recite some prayers and need some official to say 'te absolvo'. And there is a complicated theory about http://en.wikipedia.org/wiki/Superior_Orders .



  • Yeah, this is why libraries are supposed to have architects, and teams are supposed to have leads, etc.  What a complete fuster-cluck.  Somebody should have had the nads to say, no, that's not the way it's supposed to be done, and the original prat who argued that the arguments were "in the wrong order" should have been escorted out back along with a phonebook and a fire hose and had it explained to him in excruciating (and I do mean excruciating) detail.  What were the QA costs involved in testing each of the half-million overloads?  They were tested, weren't they?

    Rule #1 : Low-level implementers do not get to argue with the design.

    Rule #2 : See rule #1.

    The last time one of my guys argued with a design, I explained it in two words, to wit, and I quote: "Shut up."

     



  • Personally, I would have also added a generated comment: This brute force overloading was mandated by <list of fools> because nobody could be bothered using the underlying six base functions. It is auto-generated. The real code is in xxx.

    I admire your willingness to take credit for doing mandated stupidity in such an efficient manner.



  • This explains so much. The Windows API never does anything simple. Any simple methods that do exist are obsolete and "should not be used in new code". After all, GetFoo(FooID) is too simple, clearly we need more control, so use GetFooEx(FooID, FooType, UserID, InstanceHandle, DesktopHandle, UserHandle, TheFooYouAreTryingToGet, Version, Flags, Reserved1, Reserved2) instead.



  • @snoofle said:

    I admire your willingness to take credit for doing mandated stupidity in such an efficient manner.

    No, efficiency would have been using the newly-available C# 3.0 "params" keyword. At the time, I didn't know about it at the time (C# 3 was, as I said, new)... but knowing about it now, we only needed two overloads for each function.

    TheFunction(...all seven parameters in One True Order...)
    {
    // real code for the function 
    }

    TheFunction(params object[] arg)
    {
    // sensible default values for all seven parameters
    // foreach(object cur in arg) { if(cur is type) defaultWhatever = cur; ...and so on for the other five... }
    // check the required arg and throw an exception if not provided
    TheFunction(...all seven parameters in One True Order...);
    }

    That's the .NET way of doing this. Writing a script to generate half a million overloads, while better than doing it by hand, is the sort of thing a Perl programmer would do.

    It's also a Kool-Aid thing. When I was writing the script that wrote the overloads, somehow or other I had been convinced that this really was a Good Idea, and part of me was wondering why more companies don't do this. The wake-up call came the next morning, when I proudly displayed my work to the guy in the next office, and his abject horror simply could not be concealed. It took me about ten seconds to figure out that no, he was not a moron... I was. I'd been happily drinking the Kool-Aid and buying into whatever they handed me, and this ended up happening.

    But honestly, it's very good Kool-Aid. I mean, imagine giving your parameters in any order. It makes so much documentation unnecessary. It makes remembering what to do so simple. How often do you have a method you call all the time, and there are four or five parameters between the parameters you know and the parameter you need that have to be manually set to defaults? You can't usually say "SetThisAndThat(here, there, specialflag)". You have to say "SetThisAndThat(here, there, 0, 0, -1, null, systemConfig ? EFFECT_NONE : EFFECT_NORMAL, specialflag, false)".

    Dammit, I'm doing it again.



  • @CDarklock said:

    No, efficiency would have been using the newly-available C# 3.0 "params" keyword. At the time, I didn't know about it at the time (C# 3 was, as I said, new)... but knowing about it now, we only needed two overloads for each function.

     

    Uh.... the params keyword has been around since at least .NET 1.1. 



  •  To misquote a line from The Green Mile... "When I die and I stand before God awaiting judgment and he asks me why
    I wrote that code, what am I gonna say, that it was my job?"



  • Well, some languages support named parameters ... Apparently, C# does not. Or did I miss something?



  • Perhaps I've misunderstood the original poster, but wouldn't this only work if every single parameter was a unique data type?

    Doesn't exactly seem like a general-purpose "solution".



  • @Zylon said:

    Perhaps I've misunderstood the original poster, but wouldn't this only work if every single parameter was a unique data type?

    Doesn't exactly seem like a general-purpose "solution".

    @CDarklock said:

    And then they noticed that all the parameters were different types, so the compiler didn't care, and we could generate multiple versions handling all the desired orders.



  • @Morbii said:

    Uh.... the params keyword has been around since at least .NET 1.1. 

    It appears you're right. When I came across it for the first time last year, I was looking it up on MSDN when the code's author came by and said it was new in C# 3.0; given where I was working, I took his word for it. However, a quick search reveals that Java was adding a similar feature in 2003 which was being compared (unfavorably) to C# "params" on a few tech blogs.

    @Zylon said:

    wouldn't this only work if every single parameter was a unique data type?
     

    Yes:

    "And then they noticed that all the parameters were different types, so the compiler didn't care, and we could generate multiple versions handling all the desired orders."

    And just to make it clear, I would never recommend this approach as a general solution. 😉



  • The idea isn't entirely new.  There's been a standard for functions with variable arguments for damn near 20 years in C.  And even if you've never used it, every programmer should at least have some inkling of the fact that it exists -- printf() uses the same mechanism.

    This is one of those WTF moments where you should have stopped and thought, "There's GOT to be a better way."

    When your solution is "I'll write a script to generate half a million method signatures", your solution is wrong.



  • Actually seems like the best solution here would be something like VB's named arguments.

    Could have also used a struct/class for the arguments, although in this case it may have just shifted the debate toward the constructor parameters.

    Next best solution, IMO, would be to go back to the person/people who requested it and say "That's completely retarded.  The library stays as is."  If they want their own little customized overload/shortcut then there's no reason why they can't just write it themselves and have it call the "real" one.

    Also, you didn't specify a language here but a lot of them support generics, which could have saved the trouble of maintaining different versions for each control type.  Or, again, people could just write their own shortcut methods.  I'm sure there'd still be less than 500,000 method signatures floating around that way.

    Just confessing that you made this unholy mess doesn't eliminate the responsibility.  Unless you're Catholic I suppose.

    Edit: Wait, I just saw that you did specify C#.  Seriously, there are so many better ways to do this, what the hell were you thinking?



  • @lolwtf said:

    This explains so much. The Windows API never does anything simple. Any simple methods that do exist are obsolete and "should not be used in new code". After all, GetFoo(FooID) is too simple, clearly we need more control, so use GetFooEx(FooID, FooType, UserID, InstanceHandle, DesktopHandle, UserHandle, TheFooYouAreTryingToGet, Version, Flags, Reserved1, Reserved2) instead.

    Of course, there are a few convoluted functions, but to claim it's all complicated, is stupid. Read the damn MSDN sometimes.

    And give an example to your claims. Otherwise you're bullshitting.



  • @CDarklock said:

    That's the .NET way of doing this. Writing a script to generate half a million overloads, while better than doing it by hand, is the sort of thing a Perl programmer would do.

    No, a Perl programmer would re-write the function to use named parameters. FindObject(type => "button", name => "foo") is the same thing as FindObject(name => "foo", type => "button").



  • @CDarklock said:

    But honestly, it's very good Kool-Aid. I mean, imagine giving your parameters in any order. It makes so much documentation unnecessary. It makes remembering what to do so simple. How often do you have a method you call all the time, and there are four or five parameters between the parameters you know and the parameter you need that have to be manually set to defaults? You can't usually say "SetThisAndThat(here, there, specialflag)". You have to say "SetThisAndThat(here, there, 0, 0, -1, null, systemConfig ? EFFECT_NONE : EFFECT_NORMAL, specialflag, false)".

    Dammit, I'm doing it again.

     

    It's all fun and games until you need to improve the code and backwards compatability kicks you in the groin. Having to refactor methods that accept just about everything as a parameter is hell. Also if you have methods with more then ~5 arguments,  you're probebly doing it wrong and more functional decomposition is needed.

    Yes there  is something to be said for having helper methods that for instance let you do:

    [code]Label = new Label('label1','My first label', 100, 50, LabelHandler, StyleHandler, true, false, true);[/code]

    But personally, while a lot more verbose, i prefer:

    [code]Label = new Label('label1');
    Label->setText('My first label');
    Label->setDimensions(100,50);
    Label->setActionHandler(LabelHandler);
    Label->setStyleHandeler('StyleHandler');
    Label->setAutoResize(true);
    Label->setDisplay(false); [/code]

    Yes it takes more space, but it's a hell of a lot more readable. You might say that IDE's or API documents can show you what every parameter is, but i prefer to just be able to read it at a glance.



  • @stratos said:

    Label = new Label('label1');
    Label-&gt;setText('My first label');
    Label-&gt;setDimensions(100,50);
    Label-&gt;setActionHandler(LabelHandler);
    Label-&gt;setStyleHandeler('StyleHandler');
    Label-&gt;setAutoResize(true);
    Label-&gt;setDisplay(false);

    Ew!

     

    How about something like:

        my_label->text = 'My first label';

     

    Or better yet:

        my_label = new Label((
    'text' => 'My first label',
    'display' => false));


    I would say that is much clearer and less-verbose.  Of course, the language has to support stuff like class properties, named params and/or native hash literals.



  • I hope you took a 129 hours break (over 14 working days). Thats what it'ld take if you took 1 second to write each method minus an hour to create the generation code. Add triple of this for "testing" of course, bringing it to over a 519 hour break! Personally I'ld make sure that whoever made you do this knows how much time you could be wasting by doing something so stupid. Actually wasting that time is 1 step better!



  • @lolwtf said:

    This explains so much. The Windows API never does anything simple. Any simple methods that do exist are obsolete and "should not be used in new code". After all, GetFoo(FooID) is too simple, clearly we need more control, so use GetFooEx(FooID, FooType, UserID, InstanceHandle, DesktopHandle, UserHandle, TheFooYouAreTryingToGet, Version, Flags, Reserved1, Reserved2) instead.
     

    Ahhh... A couple of true signs of a know-nothing script kiddie:

    1. Senseless bashing of Microsoft and Windows

    2. Thinking the Windows API "never does anything simple", or in fact is complicated at all for the most part.

    3. Your entire post.

    Thanks for confirming that for me; I've been suspecting it for all of your other 120+ posts. Back to JavaScript with you now.



  • @morbiuswilters said:

    How about something like:

        my_label->text = 'My first label';

    I prefer setters. In above way, depending on the language, i can not add extra handeling code in between the assignmen.So that makes creating decorators & Co harder.


    Or better yet:

        my_label = new Label((
    'text' => 'My first label',
    'display' => false));



    I would say that is much clearer and less-verbose.  Of course, the language has to support stuff like class properties, named params and/or native hash literals.

     

    Perhaps i'm not familiar with languages where you can do the above in a sane way, but if it means creating stuff like

    [code]void Label(Array Params){ .... }[/code]

    Thanks, but no thanks.That's exactly the kind of stuff that creates hard to use interfaces.



  • @stratos said:

    Perhaps i'm not familiar with languages where you can do the above in a sane way, but if it means creating stuff like

    <font size="2" face="Lucida Console">void Label(Array Params){ .... }</font>

    Thanks, but no thanks.That's exactly the kind of stuff that creates hard to use interfaces.

    Educate thyself.




  • @stratos said:

    @morbiuswilters said:

    How about something like:

        my_label->text = 'My first label';

    I prefer setters. In above way, depending on the language, i can not add extra handeling code in between the assignmen.So that makes creating decorators & Co harder.

    A "property" is a magic thing that lets you treat something syntactically as if it were a direct reference to an object member, while actually using getters and setters. For example, in a hypothetical C++-like language:


    class foo
    {
        int property bar
        void bar_set(int i) {if(i > 0) bar = i else bar = -i}
        int bar_get(void) {return bar}
    }
    
    foo baz
    baz->bar = -7 // Note: This is equivalent to baz->bar_set(-7)
    


  • @Carnildo said:

    @stratos said:

    @morbiuswilters said:

    How about something like:

        my_label->text = 'My first label';

    I prefer setters. In above way, depending on the language, i can not add extra handeling code in between the assignmen.So that makes creating decorators & Co harder.

    A "property" is a magic thing that lets you treat something syntactically as if it were a direct reference to an object member, while actually using getters and setters. For example, in a hypothetical C++-like language:




    Which is why i wrote "depending on the language".



  • ASP.NET MVC uses the new anonymous classes to simulate this:

    someObject.DoSomething(new { Name = "Something", Type = "Some Type" });

    perfectly valid C# 3.0, just takes some more work on the DoSomething method 🙂


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.