Ben's profileNo Brain, No PainPhotosBlogListsMore Tools Help

Blog


    September 24

    Things that make me chuckle, part II

    Last week a colleague of mine pointed me to SharpDevelop, which is a free .Net IDE.  Personally I wasn't too interested in it since Visual Studio 2005 works just fine for me, but nonetheless I dug around the site a bit more unitl I stumbled into something that piqued my curiousity more than a little: the SharpDevelop C# Coding Style Guide 0.3.  Normally, I take coding guidelines with a grain of salt; usually they turn out to be egotistical exercises in proclaiming how much better the world would be, if only everyone else made their code look just like mine!
     
    This document, though, seemed different from the typical coding guidelines.  It announced it could "...be read as a guide to writing robust and reliable programs." 
     
    That last line resonated within me, since I've spent the last decade tinkering with different approaches to improve robustness and reduce defects in software.  I started to download the file, with my curiousity building:  had they come up with new way to simplify state management?  Perhaps a new, novel approach to exception management?  Maybe something I had never considered before?
     
    Hahahahaha!  Yeah, right.  It was just another document with the same old junk about what line your curly braces should be on, where you should use Pascal casing vs. camel casing, blah, blah, blah.
     
    Don't get me wrong, it's nice when code looks consistent, and it's something to strive for.  But do people really fool themself into thinking that a program will be more reliable and robust because of whether they start their variable name with an uppercase versus lowercase letter? 
     
    When was the last time you were debugging a program and said, "Oh, I see the problem!  I should have put another blank line between those two statements!"
     
    Yeah.  Me either.
     
    Ben
    September 22

    Why don't American car companies want to sell a car to me?

    While I'm not currently in the market for a new car, I'm always keeping an eye open as to what's currently available, in case the Jetta develops some major malfunction and I decide to cut my losses by just dumping it.  But, before I consider a car, it has to meet my basic feature list that I've been using since 2000 or thereabouts:
     
    • 6-speed manual transmission
    • engine with variable valve timing (later ammended to "interesting technology")
    • seat heaters (for the wife!)
    • 4-wheel disc brakes
    • under $20k
    • 4-wheel independent suspension

    When I was buying my last car in 2003, the car that came closest to my list was probably the Nissan SE-R Spec V, which missed out on the independent suspension and seat heaters.  The other two cars I seriously considered were the Jetta (missing out on the 6-speed and independent suspension, but pretty close to the $20k price limit) and the Subaru WRX (missing the 6-speed and seat heaters, and really too far from $20k for serious consideration).

    In the end I picked the Jetta over the Nissan because the Jetta came across more as "premium" small car while the Nissan seemed more like a cheap car with an uprated suspension and engine.  Four and a half years later, I still feel the same--I think I would have been disappointed with the low-cost feel of the Nissan, although I'm certain it would have been much more reliable with the Jetta.

    These days, I think the car that most closely matches my criteria is the Mazdaspeed 3.  It's not under $20k, but it's reasonable close (and I realize that $20k is pretty unrealistic for a car these days) and it's as quick as our Corvette.

    So how does this all relate to American car companies?  Simple:  As you've probably noticed, I haven't mentioned a single American car that's come close to my new car criteria. 

    And that bugs me, because I really want to buy an American car.  I'd much rather buy an American car than a foreign one, but nothing they offer was close enough to my requirements to merit consideration.  The biggest problem for me is that the American car companies offer very few cars with manual transmissions.  I'd often say, "Cavaliers and Corvettes"--those were the only two Chevrolet cars offered with manual transmissions; everything else was strictly automatic.

    In 2003, probably the closest American car to my criteria was the Dodge Stratus R/T, but the MSRP on that was like $27k.  I think the final price on that Stratus would have been close to the $21.5k I spent on the Jetta, but even thinking about trying to get a car salesman down from $27k makes me feel ill--the endless "let me run this past my manager" smoke breaks, the "I'm losing money on this deal" BS, the constant pushing of unwanted paint treatments.

    I think that at the time, American car companies were trying to make customers feel like they got an expensive car for an incredible discount.  But it backfired for customers like me, because I never stepped into a Dodge dealership since I didn't want to deal with hours of salesman BS while they whittled down the price $200 at a time.

    Dodge, are you listening?  Do you care?

    I'm not sure why the American car companies don't want to build a car that I'd consider buying.  Certainly I'm in a vast minority since I prefer a manual transmission.  But the German and Japanese manufacturers have no problems offering a variety of cars to me with manual transmissions.  Why don't the American manufacturers?  I guess it's because the sales volume of manual tranmission cars would be low enough that they can justify losing that business.

    Oh, well.  Perhaps someday they'll building something that comes close enough to my list that I'd consider buying. 

    I'm not holding my breath, though.

    Ben

    September 19

    Why I dislike object-relational mapping, Part II

    Here's another reason I'm not a fan of object-relational mapping fan:
     
    It doesn't leverage the strengths of SQL database servers
     
    I'm continually blown away by the performance levels of modern SQL database servers, whether it's Oracle, SQL Server, DB2, even MySQL.  It always amazes me how you can join multiple 50 million row tables, filter and sum the results, and if it takes over a couple hundred milliseconds you immediately think, "Hmmm, l bet we don't have the proper indexes".  The progress database developers have made with query optimizers over the last 30 years is just phenomenal, at least in my opinion.
     
    Fans of object-relational mapping, though, are less interested in what a database server can do, and instead are only interested in how the database can provide object persistence.  What's more, the traditional one-object-to-one-table paradigm pretty much tosses out a bunch of SQL concepts such as joins and aggregate functions.  Rather, SQL queries tend to be simplistic, such as "SELECT OrderNumber, OrderDate, this_field, that_field, etc FROM Order WHERE OrderID = 12345".  Then a small chunck of code takes the data returned from the query and plops it in the object's attributes.
     
    But ignoring SQL database functionality like this is a major mistake in my opinion.  Let's look at a simple example, based on an the order and its associated attributes from my previous post on object-relational mapping:
     
    A user comes to you with a practical request:  they want an item on their dashboard screen that displays the total value of "rush" orders currently being processed.  This would give them a quick way to determine if any exceptional orders were being processed that necessitated special care; in other words, if you've currently processing a few $100 rush orders, you'd be confident that your fulfillment process will handle them fine, but if you see $5,000,000 in rush orders, you might want to personally monitor the fulfillment process there's a high risk associated with filling these orders.
     
    First, here's my rough object-relational implementation.  I'm sure I could probably streamline it more, but I think anyone who's familiar with object-relational mapping would recognize the basic gist:
     
        class Order
        {
            public int OrderID;
            public string OrderNumber;
            public OrderLine[] OrderLines;
            public OrderAttribute[] OrderAttributes;
        }

        class OrderDataLayer
        {
            public static Order RetrieveOrderByOrderID(int orderID)
            {
                Order result = null;

                SqlConnection connection = new SqlConnection();
                connection.Open();

                string sql = "SELECT OrderID, OrderNumber FROM Order WHERE OrderID = " + orderID.ToString();
                SqlCommand cmd = new SqlCommand(sql, connection);
                SqlDataReader reader = cmd.ExecuteReader();

                if (reader.Read())
                {
                    result = new Order();
                    result.OrderID = Convert.ToInt32(reader["OrderID"]);
                    result.OrderNumber = Convert.ToString(reader["OrderNumber"]);
                    result.OrderAttributes = OrderAttributeDataLayer.GetOrderAttributesForOrderID(orderID);
                    result.OrderLines = OrderLineDataLayer.GetOrderLinesForOrderID(orderID);
                }
                reader.Close();
                connection.Close();
                return orderID;
            }

            public static Order[] GetRushOrders()
            {
                Order result = null;
                SqlConnection connection = new SqlConnection();
                connection.Open();
                string sql = "SELECT OrderID FROM OrderAttribute oa JOIN Attribute a ON oa.AttributeID = a.AttributeID WHERE a.AttributeName = 'rush'";
                SqlCommand cmd = new SqlCommand(sql, connection);
                SqlDataReader reader = cmd.ExecuteReader();
                List<Order> tempList = new List<Order>();
                while (reader.Read())
                    tempList.Add(RetrieveOrderByOrderID(Convert.ToInt32(reader[0])));
                reader.Close();
                connection.Close();
                return tempList.ToArray();
            }
        }
     
        class OrderLine
        {
            public int OrderID;
            public int ProductID;
            public int Quantity;
            public double UnitPrice;
        }

        class OrderLineDataLayer
        {
            public static OrderLine[] GetOrderLinesForOrderID(int orderID)
            {
                SqlConnection connection = new SqlConnection();
                connection.Open();
                string sql = "SELECT OrderID, ProductID, Quantity, UnitPrice FROM OrderLine WHERE OrderID = " + orderID.ToString();
                SqlCommand cmd = new SqlCommand(sql, connection);
                SqlDataReader reader = cmd.ExecuteReader();
                List<OrderLine> tempList = new List<OrderLine>();
                while (reader.Read())
                {
                    OrderLine orderLine = new OrderLine();
                    orderLine.OrderID = Convert.ToInt32(reader["OrderID"]);
                    orderLine.ProductID = Convert.ToInt32(reader["ProductID"]);
                    orderLine.Quantity = Convert.ToInt32(reader["Quantity"]);
                    orderLine.UnitPrice = Convert.ToDouble(reader["UnitPrice"]);
                    tempList.Add(orderLine);
                }
                reader.Close();
                connection.Close();
                return tempList.ToArray();
            }
        }

        class OrderAttribute
        {
            public int OrderID;
            public int AttributeID;
        }


        class OrderAttributeDataLayer
        {
            public static OrderAttribute[] GetOrderAttributesForOrderID(int orderID)
            {
                SqlConnection connection = new SqlConnection();
                connection.Open();

                string sql = "SELECT OrderID, AttributeID FROM OrderAttribute WHERE OrderID = " + orderID.ToString();

                SqlCommand cmd = new SqlCommand(sql, connection);
                SqlDataReader reader = cmd.ExecuteReader();

                List<OrderAttribute> tempList = new List<OrderAttribute>();

                while (reader.Read())
                {
                    OrderAttribute orderAttribute = new OrderAttribute();

                    orderAttribute.OrderID = Convert.ToInt32(reader["OrderID"]);
                    orderAttribute.AttributeID = Convert.ToInt32(reader["AttributeID"]);

                    tempList.Add(orderAttribute);
                }

                reader.Close();
                connection.Close();

                return tempList.ToArray();
            }
        }

    And finally, the code to display the total of all rush orders:

    public static void DisplayRushOrderTotal()
    {
         Order[] rushOrders = OrderDataLayer.GetRushOrders();

         double totalValue = 0.0;

         foreach (Order order in rushOrders)
              foreach (OrderLine orderLine in order.OrderLines)
                   totalValue += orderLine.Quantity * orderLine.UnitPrice;

         Console.WriteLine("Total rush order value = $" + totalValue.ToString());
    }

    Now let's try that again, ignoring all the object-relational mapping stuff and just using the SQL SUM() function 

    public static void DisplayRushOrderTotal()
    {
         SqlConnection connection = new SqlConnection();
         connection.Open();

         string sql = "SELECT SUM(ol.Quantity * ol.UnitValue) FROM OrderLine ol JOIN OrderAttribute oa ON ol.OrderID = oa.OrderID JOIN Attribute a ON a.AttributeID = oa.AttributeID WHERE a.AttributeName = 'rush'";
         SqlCommand cmd = new SqlCommand(sql, connection);
         double totalValue = Convert.ToDouble(cmd.ExecuteScaler());

         connection.Close();
         Console.WriteLine("Total rush order value = $" + totalValue.ToString());
    }
    Now, ask yourself the following:
     
    • Which do approach is more clear and concise?
    • Which could be developed faster?
    • Which would perform better?
    • Which would be easier to debug and fix?

    Finally, ask yourself this:  Which is the better approach?

    Ben

    September 18

    Summer wrap-up

    Just got my Jetta back from the dealer for the latest recall, this one related to a faulty brake light switch.  It wasn't too big a deal for me, since I don't have an automatic and therefore I wasn't stuck like some poor soul in a parking lot with the transmission refusing to come out of Park.  But I had noticed on a few occasions that the brake light would stay on after the brake pedal was released, with a particularly funny episode occuring when I was walking away from the car in a parking lot, only to notice the brake lights were still on!
     
    So we're now at the 11th non-scheduled maintenance item in 58,515 miles; one item every 5,320 miles.  What's worse is that the rate is increasing:  since January 1st there's already been three problems, and we're just into the 9th month of the year--something breaking every three months, instead of the usual five.  I'm such a sucker for punishment.  And to think the service advisor told me, "the car looks great for 50k miles, you really take good care of this car."  Yeah, I do... but it repays me by breaking even more frequently.
     
    To put things in perspective, if you took EVERY other car I've driven in the past 23 years and summed up all times those cars had to go to the shop, the grand total would be less than the Jetta in four years.
     
    But it turns out that's just strengthening my resolve--at first I was determined to get 120k miles out of this car, but now I'll be damned if I don't get 180k.  By that time it'll probably be in the shop 6 days out of every week :-p  I WILL PREVAIL!!!!
     
    Then I'm gonna drive the damn thing off a cliff.
     
    In other news, I did fix the air conditioner.  My first thought was that perhaps the thermostat had broke, but I stood by the compressor unit outside while someone turned on the air inside--and I heard the distinct click of a relay.  Hmm, seems like the thermostat was working.  So I dug around the internet a bit and came to the conclusion that the fan/compressor run capacitor was bad.  Since I don't have a tester that can test capacitors that large, I just decided to replace it since a new capacitor was only $30.
     
    So I put in the new capacitor, turn on that air conditioning, and...  still nothing.  Grrr....
     
    Next, I check what I should have checked first (this is a constant motif in my life):  make sure the compressor unit is getting power.  Of course, it wasn't.  So then I open up the circuit breaker panel, hmm, everything looks ok... oh wait...  why's that one wire look all burnt?
     
    Somehow the circuit breaker for the air conditioning compressor shorted internally, and melted half of the switch--I'll have to put up some pictures, I can't believe the house didn't burn down.  So $62 (!!!!) later I have a new circuit breaker, and the air conditioning starts working again!
     
    Let's see, what else.  The sewer pipe that was dug up about 8 years ago had to be dug up and replaced, AGAIN.  There goes $2.2k down the drain (get it?  down the drain?  Perhaps I shouldn't quit my day job quite yet???).  You know how everyone used to talk about DINKs?  I always joke that we're SILK:  Single Income, Lots of Kids.  It's brutal in this day and age.
     
    I also change the Corvette diff fluid a couple of weekends ago, not too bad of a job at all.  I went with Mobil 1 gear lube and a single bottle of GM limited-slip additive, and so far everything's working great.
     
    Actually it's been a real low mileage year for the 'Vette, we haven't even put 1k miles on the car this summer.  I had been purposely keeping the mileage low this summer, as I planned to take the car up to the U.P. for our annual trip, but in the end we needed more space and it stayed home in the garage.  Oh well, there's always next year!
     
    Toodles!
     
    Ben
    September 17

    Why I dislike object-relational mapping, part I.

    I've been meaning to write some of my thoughts on object-relational mapping for months now, but I kept running into a problem: there's so many reasons I think it's a bad idea that I could never come up with a coherent post that covered the main points I wanted to make.
     
    Come to think of it, I'm not sure how many of my posts could be considered coherent, but that's a whole different matter :-p
     
    Anyway, I decided to break up the huge object-relational post into several posts, each focusing on a specific problem I've seen.  So, let's go!
     
    Object-Relational mapping problem #1:  It compromises your object model
     
    Let's take a pretty simple and common example:  say I have an object (class, struct, whatever) representing an order object that will be stored as a row in the Order table of a relational database.  Also, there's an Attribute table in the database of order attributes that can be assigned to an order, e.g. "rush order", "ship together", things of that sort.  I'd like to program this as straightforward as possible, something on the order of this:

    public class Attribute
    {
         public int ID;  
         public string Name;
    }

    public class Order
    {
         public int ID;
         public Attribute[] Attributes;
    }

    But there's a problem--since this is a many-to-many relationship, we have to implement that relationship in a relational database as an intermediate table.  So the database will look something like this:
    CREATE TABLE Order
    (
    OrderID INT
    )

    CREATE TABLE Attribute
    (
    AttributeID INT,
    AttributeName VARCHAR(30)
    )

    CREATE TABLE OrderAttribute
    (
    OrderID INT,
    AttributeID INT
    )
    Now, since one of the tenants of object-relation mapping is that each class maps to a table in the database and vice-versa, that means we need to introduce another class in our code:
    public class ObjectAttribute
    {
    public int OrderID;
    public int AttributeID;
    }
    and I will also need to change the Order object:
    public class Order
    {
         public int ID;
         public OrderAttribute[] OrderAttributes;
    }
    Not a big change, right?  Well, no, not huge, but it did introduce a few small complicaitons:
     
     First, it's making our code less resemble the problem domain; end-users deal with orders and attributes assigned to those orders, not some OrderAttribute concept stuck in the middle--that's just an implementation necessity.  We should always strive to make the structure of our programs match the reality of the problem they're designed to solve as close as possible.
     
    Second, it's adding code to the project without bringing additional functionality.  A little red flag should always go up in your head whenever you're writing additional code which doesn't directly help solve the problem at hand.  Often, you can't avoid code like this as it's required for housekeeping or the like.  But you should always ask the question, "do I really need this code?"
     
    Third, it complicates any code that uses this object, as it now has to deal with dereferencing the intermediate object.  For example, you'll have to write
    foreach (OrderAttribute oa in order.OrderAttributes)
    {
         Attribute = OrderAttributes.GetAttributeByID(oa.AttributeID);
         // do something with the attribute here
    }
    instead of the more clear and conside code you'd rather write:
    foreach (Attribute attribute in order.Attributes)
    {
         // do something witht he attribute here
    }
    Certainly, none of these are deal-breakers that would cause your program to fail.  But even a medium-sized program will have perhaps a dozen instances of structures such as these, with larger programs having hundreds to thousands.  And each instance makes the program a little bit harder to build and a little bit harder to maintain.  It's not a question of whether this will work or not, as there are thousands of working, functional programs out there that use these object-relational mapping strategies..
     
    Rather, the question is this:  Could they have been better?
     
    I'm sure some people reading this are thinking, "Well Ben, regardless of anything you still have to implement this in the database with the three tables you described, and somewhere you'll have to deal with that complexity somewhere else in your code."  Yup, absolutely true.
     
    My solution to this would be to place that complexity in the code that is responsible for saving the order data (and the attribute linkage) to the database, e.g. a Save() method in the order class.  Although I'm a self-professed object-oriented heretic, there are certain concepts that I consider extremely valuable, such as encapsulation.  In my solution, the complexity of dealing with the intermediate database table is encapsulated into the order object save code.  To me, the traditional object-relational mapping approach is just the opposite, needlessly forcing inplementation complexity on the consumers of the object.
     
    Ben
    September 16

    My take on the McLaren scandal

    In a nutshell, I thought the penalty McLaren received was appropriate.  I think excluding them from the 2008 championship would have gone over the line.  A lot has been made about how "draconian" the penalty was, but I found it no more severe than some of the penalties Schumacher saw in 1994.  I mean, the FIA could have just said, "Ok, McLaren is banned from competing in the next two races", which would essentially hand over the driver and constructor's championships to Ferrari.  And that would be a shame, since this title fight is the most interesting one for years.
     
    I was really disappointed with Speed's broadcast team during coverage of Friday's practice.  As my buddy Kurt said, "They threw Alonso under the bus."  For an hour they bitched and moaned about Ferrari and Alonso as if this was all a conspiracy against McLaren... then a few hours later the FIA documents came out, displaying for all to see exactly how guilty McLaren was, and how completely misguided the Speed crew was.  Personally, I think the Speed crew should offer their viewership an brief apology for their incorrect and irresponsible "reporting".
     
    I did get a kick out of how on Friday Matchett said (I'm paraphrasing here, don't remember the exact quote), "If I were a McLaren mechanic, I wouldn't put the starter in the car" while during the Sunday race broadcast he stated how he didn't think the FIA had reason to worry about how McLaren would treat Alonso for the remainder of the season.  So is McLaren completely against Alonso, or completely aligned with him?  Pick a side, please!
     
    I think many have gotten way too hung up on whether or not McLaren benefitted or not from the Ferrari information.  Certainly no Ferrari parts ended up on the McLaren car, and it seems that a a good portion of the information they received wasn't relevant to the McLaren chassis.  But I think they're missing the point:  this is a question of ethics, not a question of whether or not they benefitted from clearly non-ethical behavior.  For example, say someone came up to you and said, "take this hundred dollar bill I stole."  Is that only wrong if you spend that money?  Or, an even better example, look at how Pepsi handed a situation where Coke employees tried to sell them the Coke formula.  Had McLaren done that, they would have looked like heroes.
     
    At the end of the day, I think Ron Dennis finds himself in a no-win situation:  Either he knew what was going on and lied continuously to the FIA, or alternatively he's clueless and has no idea what's going on within his organization.  With regards to the latter, James Allen made the point that McLaren is a large organization, and certainly Ron can't know everything that's going on.  But, it's not like it was the mailroom clerk and the receptionist doing this, it was his chief designer and World Champion driver!  Are we supposed to believe he doesn't have any idea what the highest-profile members of his team are doing?  If not, what the hell are they paying him for?
     
    If nothing else, it's a good example for the kids: It takes a lifetime to build a good reputation, but only minutes to destroy it.
     
    Btw, boring race today other than Sutil in the Spyker, that guy shows some incredible promise.
     
    Ben
    September 14

    Microsoft ISA Server 2006

    Many years back, I used to use Microsoft Proxy Server 2.0 in order to share a modem internet connection.  It worked well, but when we moved up to a cable modem I eventually got a Linksys BEFSR81 router and used that instead.  The Linksys router turned out to be pretty flaky when you started using it for anything beyond the most simple configurations, so when I started at working at CDW, my first "employee purchase" was a Netgear FVS318 router.
     
    I used that router for about 5 years with very few problems, other than I could never get the VPN capabilities working without the Netgear VPN client software.  Theoretically it should work with just the Microsoft VPN client, but try as I might, I could never get it working correctly.
     
    Alas, a couple of months ago one of the ports died on it, and shortly thereafter another port started getting flaky.  Since we're using 7 of the 8 ports on the router, this was a problem.  Hmm, what to do?
     
    To add to my quandary, I've also been wanting to update the local subnet to 1Gb/sec networking for a couple of years now.  Two of the most recently built machines have 1Gb/sec networking built into the motherboard, but since the router was 100Mb/sec, everything was limited to that speed.
     
    So instead of buying another cable/DSL router, I bought a 1Gb/sec switch, installed an additional 1Gb/sec NIC on our server box, and set up Microsoft ISA 2006 server on the same box.  Here's what I've learned so far:
     
    • You need a reasonable amount of network knowledge to get ISA 2006 working.  My networking knowledge is ok, not great, and when I first installed ISA 2006, *nothing* worked.  The network card plugged into the cable modem couldn't even get an IP address.  It turns out that out-of-the-box ISA 2006 blocks *everything*, so you have to enable things like DHCP traffic from localhost to the external network before anything will work.
    • ISA 2006 should really run on a box that is solely dedicated to being an edge server.  I have one spare box that I use as server, so it ends up running everything--ISA, IIS, SQL Server, file and print sharing, blah, blah, blah.  While ISA 2006 is fine with this configuration, I've realized that putting anything other than ISA on an edge server is a security disaster waiting to happen.  I'm hoping to set up a dedicated ISA 2006 server in the next couple of months.
    • There's some issues with Windows Server 2003 SP2 and ISA 2006, mainly due to networking changes in SP2.  There's some web sites that list some workarounds, but it wasn't always obvious what exactly was the causing the problems.  I've been able to work around the problems, and I hope to have another post with some specific solutions I've found.
    • There's some wacky problems with daily ISA 2006 report generation when you're logging data to SQL Server.  I never quite figured out if the problem was due to me running SQL Server 2005 on the same box, or something else.  In any case, logging ISA 2006 to SQL Server doesn't seem to be a great idea, as the amount of data is a bit ridiculous--like 40MB of log data in SQL Server per day for my tiny network.  That amount of data would be fine if it was high value, but socket connection logs?  Do you really need that data in SQL Server?

    Overall, though, ISA 2006 server kicks butt--it's very fast even on slow hardware, and it's easy to configure access rules.  My favorite rule is one that blocks access to many of the dumb advertising sites like advertising.com, doubleclick, etc.  So, instead of getting a dozen ads on every web page, I get a green box that says "Insert annoying advertisment here". Score!

    From what I've seen thus far, I'd certainly recommend it for SMB, and probably for larger businesses, also.  It seems cost-effective compared to similar hardware solutions, and I haven't run into a downside yet.

    Plus, it generates nice HTML traffic report summaries.  So, if you're *really* bored, you can check out traffic in my basement:

    http://basementnet.dyndns.org/isa

    Have fun!

    Ben

    September 10

    A workable approach to updating legacy systems

    Here's a common problem I see over and over again: an organization has a ton of code that was written 5, 10, maybe 20 years ago that's absolutely required to run the business.  But, the codebase is in deparate need of an update for a variety of reasons:
     
    • Outdated hardware/software (e.g. 16-bit Delphi 1.0 app running on Windows 3.1)
    • Quick bug fixes and hacked-in additional functionality over the years have turned the codebase into a fragile knog of spaghetti
    • Current code doesn't provide enough scalabillity and/or reliability
    • Etc, etc, etc.

    Typically the solution I hear from developers is this: "We need to stop trying to hack in additional features and take a year or two to rewrite the app from scratch."  Then they'll usually mutter something about how management would never allow them to do something like that.

    Now, I'm a developer and not a manager, but in this case I'll side with the managers for several reasons:

    1. The cost to re-implement current functionality can't be justified because at the end of the project you'll basically have the same functionality that you started with.  But boy, you've blown through a lot of budget dollars!
    2. There's the opportunity costs of not having resources available for implementing new functionality, while (unfortunately) your competitors are.
    3. There's no guarantee that the new codebase will be any more maintainable/scalable/faster than the current codebase.  Likely?  Yes.  Guaranteed?  No.

    Alternatively, I've been approaching problems like this with a technique I loosely refer to as "software evolution."  It's deceptively simple, but over time I've found it to be a effective and workable solution.

    First, you need to set mid- to long-term goals for the system.  Usually, these are fairly generalized, broad goals such as, "we need to move such-and-such data off the mainframe and onto PCs" or "we need to improve the reliability of this interface between these two computers."

    Next you go forward with your usual workload as you have in the past, fixing bugs and implementing new features, but with one subtle difference:  you now apply the following test to every change you make to the codebase:

    Does this change bring me closer to my goal?

    If the change you have proposed doesn't bring you closer to your stated goal, don't do it!  Instead, rethink your solution until you come up with an approach that does.

    As a simple example, assume that long-term goal is to move your mainframe data onto PCs.  Your manager has just come to you, asking for an enhancement that will require a new database table.  Where will you put this new table?

    If you put the new table on the mainframe, the test will fail; the change will put more data on the mainframe, which is exactly what you're trying to get away from.  Of course, putting the data on the PC might cause some additional headaches for your current task (for example, what if you need to access this data from the mainframe?).  But it turns out there is no alternative: putting the data on the mainframe moves you further from your goal, and converting all the data en masse not practical.

    In the end, what you're doing is spreading the risk and development effort of the conversion over many small projects, with the bonus that you're continually adding new functionality or bug fixes.  And as you use this approach over time, you will slowly (but surely!) see your systems evolve into what you want them to be.

    Which is a good thing.

    Ben

    September 04

    Software gravity?

    My mind tends to wander a lot when I'm out mowing the grass.  Probably not the best time to do that, but I've been doing it for as long as I can remember.  Anyway, I was about halfway through the backyard last Saturday when an oft-quoted software analogy popped into my head: "Building software is like building a house; first you build the foundation, ensuring that it's strong enough to support the rest of the structure, then you build the remaining functionality on top of the foundation, blah, blah, blah."
     
    I always cringe a bit when I hear that analogy, as I never really saw many parallels between building software and building a house.  What part of a house does Javascript client-side validation correspond with?  Gutters?  Or perhaps, more interestingly, what part of software program is represented by a house's sewer pipe?  Enquiring minds want to know!
     
    Anyway, I continued to think about the analogy, trying to come up with some good points in order to refute it.  I looked at it from a housing perspective--maybe, perhaps, you don't need to build the foundation first, you could build the house in an alternate order with no penalty.  But, after thinking about it for a few minutes, I couldn't come up with any way to build a house that didn't involve putting the foundation in place first.  Sure, you could pre-build the walls, but you couldn't put them into their final position until the foundation is poured.  And the roof can't go on until the walls are up.  And so on, and so on.  Thus, I convinced myself that a house has to be build with the foundation first.  So next I asked myself, "why?" 
     
    The answer, of course, is simple:  gravity!  Since gravity is trying to pull everything into the ground, the foundation has to be built first, as gravity will pull the remaining pieces of the house down onto the foundation.  And the foundation has to be robust, since it has to support the entire weight of the house.
     
    And that's exactly why the original analogy is flawed--software is pretty much immune to the effects of gravity.  If you were building software for the space shuttle, you'd still build it the same as you would for an application running on earth.  The same can't be said for building a house.
     
    Nonetheless, I kept thinking; the concept of building the foundation first in order to counteract gravity is a sound principle.  Perhaps that principle could somehow be applied to building software?
     
    I believe it can.  The problem then becomes trying to understand what force software is trying to oppose.  Certainly it's not a physical force, like gravity.  But it seems reasonable that software's job is to work against some type of conceptual force.
     
    Alas, I can't seem to put my finger on what this "force" is, at least not yet.  Any suggestions?
     
    Ben