The registration system - Part 1



  • I'll post the actual code in part 2... but I wanted to lay a good foundation for the story in part 1. 

    I've been a developer for 10 years now. In that time, I've had the opportunity to work with a handful of individuals (four to be exact) that I would patently refuse to work with ever again. I've never actually met one of the individuals on that list... he got there based on the code I had to clean up and the stories I've heard told about him.

    This individual was the mastermind behind the design and implementation of a simple event registration system. You know, look at upcoming events, register yourself, a guest or a group, tell us a bit about yourself, that sort of thing. There was an administration side as well. Someone could go in, create new events, copy existing events as a template for new ones, look at who registered and who attended. The system didn't have to handle repeating events, multi-day events, or anything remotely complex. The basic pattern was "read from a database, display, write to the database".

    Simple. Well, it should have been simple. You see, this person got pattern-happy. Personally, I like patterns as much as the next architect. I tend not to think of them in isolation or to even go out of my way to force them into my programs. Rather, for me and most people I know, it's more of an organic process... you start muddling over how to crack a particular problem and then you realize you've seen the solution before. No, not this guy. Instead, he was proud of the fact that his system contained every pattern in the GOF. Nevermind that his implementation was flaky, at best, and didn't properly implement many of the patterns... facts like that didn't matter to him.

    So, a little more about "the system" as we came to call it. In the grand tradition, it's all C#... but don't let that detract you from the true horrors. The system was a series of reusable components used in three different websites. The websites were each about 50k-100k lines of code. The system, a functionally small subset of each site, weighed in at 98k lines of code spread across 17 visual studio projects. To top it off, there wasn't a unit test in sight.

    The very first time the system was turned on for QA, it turned out that it could safely handle one concurrent user before causing the app pool to throw up. Now, I don't know about you, but I don't think a website intended for mass usage should force serialization... we call those desktop apps. So, a massive rewrite was undertaken over the next two weeks. As most panic rewrites end up, I like to call this one the decorator crab. You don't actually fix anything, you just keep sticking more crap on until it looks pretty.

    Of course, none of this actually got our illustrious antagonist in trouble. Sure, the other developers weren't too keen on the guy after management constantly preaching that his pattern-oriented development was the future for the group.. heck, a few of them were just a little upset after two 100+ hour weeks to get a shippable system. But, management loved him and put him on a pedestal. No, what finally did the guy in is that he snuck a new feature into the code, didn't tell QA about it, caused a huge ruckus in production, and then tried to claim in a large meeting that someone else had logged on to his box and made the change just to frame him.

    Flash forward a year. That's where I came in. As the newest guy on group I learned three things very quickly. First, the newest senior guy on the group was always saddled with maintaining the system (so, I got it and the previous maintainer immediately disavowed all knowledge of the system, as did the programmer before him). Second, no one ever wanted to make changes to the system because QA cost to test that was higher than all of the other websites. Third, there was no money to properly fix the app so don't even bother.

    Not being one to give into the pressure of silly things like "budget" and "don't change because that's the way it's always been", I decided to challenge the system, to be its archeologist and, perhaps, to find a way to bring the team some closure and peace on the system. Sure, some people do noble work... me, I just wanted to find the elephant in the granite.



  • First?  I can't be first! 

    Okay.  I'll be first.  I like your attitude.  Find that elephant and kick it's ass!
     



  • I love the inherent WTFiness about the phrase "one concurrent user" ;-)



  • A hundred thousand lines?  How?  It sounds to me like the sort of thing I can whip up in a thousand well-commented lines of Perl.



  • [quote user="Carnildo"]A hundred thousand lines?  How?  It sounds to me like the sort of thing I can whip up in a thousand well-commented lines of Perl.[/quote]

    Ahh, but I know one could also whip it up in a hundred thousand lines of Perl as well... with the same horrible results.

     As for how it happened, that's easy... clueless dev lead attracted to patterns with no real knowledge of how to use them (someone at the peak of excitement... I'm sure the trough of disillusionment came quickly after); management attracted to buzzwords such that they were convinced the people speaking out against the system were just jealous; a fix-it-fast mentality after said system falls over. That's a sure-fire recipe for disaster every time.

     


  • Discourse touched me in a no-no place

    [quote user="Carnildo"]A hundred thousand lines?  How?  It sounds to me like the sort of thing I can whip up in a thousand well-commented lines of Perl.[/quote]

     

    As if there's such a thing as "well-commented [...] Perl."

     



  • ### mm_init

    Initializes base configuration variables in the main configuration

    object. Sets reasonable defaults for unspecified settings, then

    allows configuration file settings to supercede, then environment

    variables to supercede, and finally parses command line arguments,

    which override any other conflicting settings.

    Takes and returns a configuration object (MMConfig)

    sub mm_init {
    my $cfg_obj = shift || return undef;
    Getopt::Long::Configure("bundling", "no_ignore_case");

    Set some defaults

    my $daemonize = '';
    my $quiet = '';
    my $port = 27415;
    my $bind = "0.0.0.0";
    my $logdest = "/var/log/mm/mm.log";
    my $cfgdest = '';
    my $stderr_severity = 1;
    my $showvers = '';
    my $showhelp = '';
    my $result;
    my @argv_copy = @ARGV; ### Copy the arguments so we can restore them

    $result = GetOptions(
    "daemonize|d" => $daemonize,
    "quiet|q" => $quiet,
    "verbose|v+" => $stderr_severity,
    "logfile|l=s" => $logdest,
    "port|p=i" => $port,
    "bind|b=s" => $bind,
    "cfgfile|c=s" => $cfgdest,
    "version|V" => $showvers,
    "help|?" => $showhelp
    );
    if(!$result) {
    usage();
    exit(1);
    }
    if($showvers) {
    identify();
    exit(0);
    }
    if($showhelp) {
    usage();
    exit(0);
    }

    @ARGV = @argv_copy; #Reset the ARGV array for another pass-thru (after a HANGUP)

    $cfg_obj = mm_read_config($cfg_obj, $cfgdest);
    if(!defined($cfg_obj)) {
    die "Can't find a valid config file."
    }

    mm_process_env($cfg_obj); ## Allow environment variable overrides

    my $cfg_hashref = $cfg_obj->config();

    if($quiet) { ## If quiet is specified, set console severity
    $stderr_severity = 0; ## to 0 (Critical Errors).
    }
    $$cfg_hashref{"__STDERRSeverity"} = $stderr_severity;
    $$cfg_hashref{"__useSTDERR"} = 1; ## By default we print to stderr.
    if($daemonize) { ## If daemonize specified set some single-shot config vars.
    $$cfg_hashref{"__daemonize"} = 1; ## stderr isn't used in daemonize mode.
    $$cfg_hashref{"__useSTDERR"} = 0;
    }

    Copy any supplied command line options over config

    $$cfg_hashref{"net.port"} = $port if $port;
    $$cfg_hashref{"net.bind"} = $bind if $bind;
    $$cfg_hashref{"logDestination"} = $logdest if $logdest;

    Return the config object to the caller

    return $cfg_obj;
    }


Log in to reply