Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Re^6: Legacy Code: "Dominoes = Bad"

by sundialsvc4 (Abbot)
on Apr 29, 2011 at 22:59 UTC ( [id://902090]=note: print w/replies, xml ) Need Help??


in reply to Re^5: Legacy Code: "Dominoes = Bad"
in thread Legacy Code: "Dominoes = Bad"

I cordially suggest that perhaps you are your argument may be resting a little bit too heavily on the loaded word, “guess.”   Instead of alternates like, “anticipate,” or, “plan ahead.”   There is certainly great value in the arguments you make (as usual), but I feel that they are being carried, shall we say, “beyond the point.”   A valid point can be – has been – made, that nevertheless need not be viewed as being, so to speak, “exclusive of all other nearby points that perhaps do not go quite so far.”   (And if I mis-read you here, it is not with intent to do so.)   There is, in fact, plenty of “wiggling room” here, and indeed, the matters of just where that maneuvering room exists (and how to increase it) is the crux of my Meditation.   Let me now try to offer a particular example.

Legacy code might well have been written so spectacularly well that it perfectly accommodates both the needs and the technical limitations of its time.   It runs, say, great on a Windows for Workgroups 3.1 machine with an 8.3 filesystem, which is what it was designed for.   Whether the design anticipated the future or not, and whether (if it tried to...) it did so correctly or not, is actually somewhat of a side(!) issue.   The business requirements have changed.   The technical limits are different.   The intrinsic capacity of the operating environment is vastly different.   The application must change, and it cannot be completely scrapped.   “Starting over is not an option.”   (Let’s simply posit that “this was a mandate from On High.”)

One thing that we can certainly say about software is that it often has an amazingly long “shelf life.”   We can also say that it often was never designed to be as durable as it has become; and that it may well not have been maintained in a way that favors future maintainability.   I daily encounter a lot of code in which “this part works, only because lots of other parts happen to also work right now, too.”   Software is not particularly ductile.   The more you hammer on it, the more brittle it becomes.

Replies are listed 'Best First'.
Re^7: Legacy Code: "Dominoes = Bad"
by BrowserUk (Patriarch) on Apr 30, 2011 at 06:40 UTC
    The business requirements have changed.

    At that point, and at no time before, you have sufficient information available to accommodate that change in requirements.

    At any point before that, the expenditure of significant extra effort in an attempt to ease the accommodation of possible future requirements is nothing more than gambling.

    When you've been a part of a project that has accumulated over 800 man years of development effort, and then seen it scrapped in its entirety because of an arrogant designer who came in and decided that the project had to anticipate a bunch of future possibilities--some of which were so fanciful that you'd be hard pushed to realise them with today's hardware, let alone the 486 and pentium I processors of the time--that it pushed the first release 6 months and $100 million over budget and still it didn't meet half of the actual requirements. At that point, you get very wary of those who believe they can predict the future.

    The bottom line is that until you and others start talking in specifics--actual code samples demonstrating your definition of 'good' and 'bad' code with respect to maintenance--nothing will be resolved.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      the expenditure of significant extra effort in an attempt to ease the accommodation of possible future requirements is nothing more than gambling.

      That's why several people have noted that they aren't talking about "significant extra effort".

      I've found that my predictions for possible future requirements have been significantly accurate (that's part of my job, ya know?). But, even if I'm almost certain that feature X will be required in the near future, if supporting feature X introduces even moderate extra effort or extra complexity, then I (usually) put aside supporting that feature, mentioning "you're not going to need it".

      Just because you've been seriously burned by somebody who was bad at predicting future directions and even worse at recognizing costs doesn't mean we all have to be pathologically opposed to venturing even a little bit toward that side of the scale.

      I am pretty quick to resort to "you're not going to need it", when the predicted need seems other than very likely, when the support for that need is starting to add even moderate additional complexity or looks to require even moderate additional effort, when the understanding of what the predicted future need likely really means seems even a little fuzzy, or even when the discussion of the need or how to support it has gone on too long or even just doesn't appear to be progressing particularly fast.

      But I don't immediately shut down considerations of potential future changes with Draconian proclamations to strictly only consider present business requirements.

      And it isn't just predicting specific future needs. It is giving the design the benefit of becoming clearer and cleaner so that it is more resilient to be modified even when how it might be modified has not been predicted in any specific way.

      Bouncing potential future needs off an emerging design in order to gauge how easily it might accommodate them can be quite useful in discovering more natural boundaries between components, producing more modular pieces with more logically cohesive functionality such that each piece is less likely to require changes in future.

      When solving a complex programming problem, my most common first step is to identify some subset of the problem that makes sense to separate out as an isolated module. When that process produces something with an obvious and clear name and logically tightly cohesive functionality where the subroutine names are obvious and clear, then I'm unlikely to spend much time futzing with that design and I can just factor that part out of the larger problem and continue working on what is left.

      More often, the first choice of what to separate out isn't such an obvious "win". So I'll bat around different names trying to find the clear logical entity. But I'll also throw potential alternate uses other than the current requirements and this usually quickly leads to selecting different subsets of features and aids in figuring out a subset that isn't overly driven by the current specific mandated approach to the larger problem.

      If given a business requirement to bill customers for "minutes used", specifically rounding up to the next nearest whole minute for each call, it can certainly be far from a waste of time to consider the possibility that the currently mandated rounding method might change. If your initial approach to meeting the given requirement can't handle that, then you might have come up with a rather fragile implementation. For example, you might opt to just store the pre-rounded number of minutes and nothing more. It might be worth considering an alternative like storing the call start time and the call end time.

      If you start dreaming up the business wanting to bill in milliseconds, realize the database only stores date-times accurate to one second, and start designing how you can use proprietary high-resolution timer features to get more accurate start/end times and then debating exactly how you should store these high-res times in the DB... then somebody needs to smack you with "you're not going to need it".

      There are tons of not-very-specific uses that are easy to predict as "likely to be wanted in future" of having fairly accurate start and end times of phone calls recorded. Since you demand examples: You might want to provide Operations with a graph of how many calls came in over the last 5 minutes so that they can monitor system activity, even though that wasn't handed to you as a specific business feature requirement for this work. I have actually previously predicted that need and seen it come true. I have almost never been given a business requirement to store accurate call start/end times. Stand in utter amazement at my astounding psychic abilities. ;)

      It might also be worth considering that the business might want to charge per call (not just per minute), per phone number allocation, and/or per phone number assignment per month, even though the business has not even mentioned those possibilities to you. You certainly shouldn't start building a separate system for handling billing per call just in case the business will eventually want that. But you might decide to build the system for billing per minute in a different way such that it will likely be significantly easier to add support for billing per call to it. The mere effort of considering these alternate billing scenarios (that are easy to predict the business wanting in future because they are all quite common requirements for other businesses in the industry!) might lead to insights that actually simplify the design of the billing system, breaking it into more modular pieces that are easy to test and actually reduces the time required to build the initial version.

      A recent project required the addition of a "maximum pool size" option to each customer's account. Most similar options were just database columns directly in the "customer account" table in the database. Some other options were columns in a "customer options" table in the database. We considered those two approaches to solving this new business requirement.

      We also had the foolhardiness to consider predicting future business needs. We predicted that this would not be the last time that the business required a new option be added to customers' accounts! This led us to consider creating a table of option names and another table with a primary key of customer account_number + option_name and an additional text field to store the option value. Creating the two extra tables certainly added some minor extra complexity / effort. But, and I know this is just very hard to believe, we actually ended up needing three new account options before we had even finished that specific, small project! Our prediction came true again!

      On another project, we were working on cleaning up some designs around how we make changes to data in our database. We have a lot of custom bulk database manipulations that we end up writing for customers and these custom modifications need to honor the existing business rules. The existing business rules were mostly implemented rather simply and quite directly in the specific places where the rules mattered for the current implemented features. In predicting future business needs, we figured that new features would be required that still needed to obey existing business rules and so it might be a good idea to try to factor out the data-related business rules into modules that could then be used by the ad-hoc customer bulk operations that get implemented and also by the existing standard interface features.

      We ended up with several classes whose objects represent things like "a campaign" each of which is mostly a record from the database along with zero or more closely related other records from the database (similar to part of what an ORM provides but not using anything that would qualify as a general-purpose ORM).

      We agreed that we wanted the handle to the database not stored in such an object. That way only the few methods that actually interacted with the database would be clear because those methods would require that a handle to the database be passed in to them.

      Then we considered the possible future use case of copying something like "a campaign" from one database instance to another database instance. This presented an interesting challenge to the design as each object had knowledge of what parts of the in-memory object state were already in the database so that the changes could be "flushed" to the database either as an "insert" or an "update" or neither (because the item already matched what was in the DB) yet you could, in theory, pass a different DB handle in and the save would then be done "all wrong".

      This led one team member to propose a minor feature that attempted to address this theoretical problem in a small way. I ended up strongly rejecting that feature idea because 1) we had never yet written code to copy such a logical entity from one DB instance to another, 2) wanting to do such in the near future seemed only moderately likely, 3) the feature added a moderate amount of complexity and effort to be implemented, 4) the feature didn't seem to really solve the proposed problem fully, 5) figuring out how to address the problem well seemed like it would require significant contemplation and complexity.

      I am actually capable of realizing when a prediction of the future is problematic in lots of ways and in teaching others how to do so. I don't need any dogmatic rules about never trying to predict the future.

      So I rejected that particular feature idea. But I am still allowing at least my subconscious to contemplate whether the fragility of our class design in the face of such a theoretical need represents a real problem in the abstraction chosen. The abstraction seemed quite clear and simple. But in just writing this up and thus indirectly contemplating that problem, I feel a better abstraction already half-way formed that I suspect will actually simplify the design even further while also making it much more robust in the face of multiple database instances.

      It doesn't appear that that (hoped for) design improvement would ever have occurred to any of us had we not contemplated predicting future feature needs that had never been asked for and that even seemed rather far-fetched. We weren't even able to come up with a likely use-case for the feature until many days after it was proposed.

      Now that the design is starting to come together in the back of my mind, I'm also already seeing a lot of potential uses for things that it seems likely to enable that had not been asked for before but that will have real benefits in the face of likely future scenarios similar to past scenarios we've run into many times and had to solve in much more difficult ways.

      Trying to predict the future has been very beneficial to me over the years. Yes, I've seen people who really sucked at it and tended to get sucked into it and wasted lots of time as a result. I avoid wasting much time on it but not by refusing to ever do it. On the contrary, I consider it an important step when designing and almost always spend small amounts of time on it and have repeatedly found significant benefits from it.

      - tye        

        I am actually capable of realizing when a prediction of the future is problematic in lots of ways and in teaching others how to do so.

        And all it takes is one. One ASOB who believes he can "beat the odds", and dodge the bullets of historical evidence. That he is better than all the rest.

        Now check out the evidence of history.

        In 2004, The Standish Group produced its 10th annual CHAOS report. An analysis of why IT projects fail. They concluded that in the period covered by the 2004 report, 34% of all projects had succeeded. And that this represented an increase of 100% in the success rate over the 10 years and 40,000 projects they had analysed.

        In other words:

        • In 1994, 85% of IT projects failed to meet their objectives.
        • In 2004, 66% of IT projects failed to meet their objectives.

        When asked to summarise why that (dismal) success rate had improved, the answer was: ""The primary reason is the projects have gotten a lot smaller. Doing projects with iterative processing as opposed to the waterfall method, which called for all project requirements to be defined up front, is a major step forward."

        In other words. Don't try to plan (predict) all possible future outcomes. Write what you know is needed, and get it out there. You will quickly find how what of what you have can be improved; and what more is actually required.

        Now a word about dogma:(that [which] is proclaimed as true without proof). Example: Expenditure of time on the "maintainability of source code" saves money in the long run.

        I offer not dogma, but demonstrably good advice.

        Read, digest, and recognise that a common theme emerges.

        The longer designs and code spend:

        • in the splendid isolation of architects and programmers minds;
        • in their cutsey, designed-to-test-what-was-expected-to-happen test suites;
        • running against sanatised test data in convenient test scenarios;

        then the longer it will be before you find out the truth of reality. And

        • the harder it will be to revert the wrong guesses and implement the realities.
        • the more budget will have been expended, and the more will be required to correct your mis-predictions.
        • the less likely you are to be given the opportunity to correct your mistakes.

        Think of it this way. Two men are taken to points in a field 100 yards from a flag that is their destination. They are blindfolded and spun on their heels a few times. One is given one long look to orient himself before setting off for the flag blindfolded. The other is allowed a single brief look around every 10 paces. Who makes it to the flag first?

        And if you cannot see the truth in that, then the blinkers of your own legend and ego are doing you and your employers a distinct disservice.

        Gambling may give you personal highs when you get it right, but when the overwhelming weight of evidence is that you will guess wrong, and when the effects of those wrong guesses on you and those around you are so dire, seeking those highs is pure selfishness.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://902090]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (6)
As of 2024-03-28 14:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found