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

use modules within it for undreamed of purposes and fix it when it goes bad (and it will go bad). Unless it is allowed for in the initial design and implementation you will pay far more to "retro-fit" maintainability

By your own words, the things that will need changing are "undreamed of".

So all the effort you put in at the beginning to cover off all the possibilities that you can dream of--95% of which will never be needed--is wasted effort. And you won't have thought of the undreamed of things that are needed.

But it is almost always worse than that. All the complexity you added trying to facilitate all those things you predicted (guessed) might be needed, but never will, will make the adaptation to the one thing you didn't dream of 10 times harder.

So, now you wasted time and money up front, and it costs you 2x, 5x, ... more to make the change once you actually know what that change is.

Predicting the future is a mug's game. And the more obvious the future you predict seems to be, the harder you will be bitten and the more it will cost you when it doesn't come true.

More money has been wasted, more projects scrapped before they ever saw production, and more good money thrown after bad in the software industry because of whatifitis, and its close cousin, wouldntitbeniceifitis, than all other causes combined. And that includes crude, naive and just plain badly written code.

From experience, it is far easier to maintain, re-factor and upgrade simple, but crude code than it is to so for nicely structured but heavily nested O'Woe code written to try and cater for every possible future.

With the former you know that what code there is serves a useful purpose, even if it gets it wrong sometimes. With the latter, your first task is to try to work out what of the morass of code is actually ever exercised by real workloads, before you can begin the process of correcting its flaws or adapting it to new requirements.

Simple is always best. Even if you think something might be required at some point. Don't add it until is is required. Because you can bet your bottom dollar, that often it won't be required, but will get in the way of what actually is.


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.

Replies are listed 'Best First'.
Re^4: Legacy Code: "Dominoes = Bad"
by Boldra (Curate) on Apr 28, 2011 at 06:24 UTC
    Your argument seems to support the claim that "bad things have been done in the name of maintainability" than argue Utilitarians claim that "maintainability saves effort in the long run".

    Perhaps you're not allowing for the possibility that writing maintainable code can be done well. It can be done without significant increase in initial effort. For example using moose can both improve maintainability (adding attributes is easy, inheritance is easy), and it can save effort.



    - Boldra

      You can write bad, un-maintainable code using moose just as easily as you can without. Just because moose might be your flavour of the day today, that's no guarantee that it will be in anyone's favour in 10 years. When that day comes you've taken Perl, which a certain population of developers are proficient in, and slapped on another layer which reduces your 'proficient' developer pool down to a much smaller fraction (if they exist). Then you'll have monks posting about all the crapped up legacy moose code that nobody can maintain and the cycle will repeat all over again.

      Legacy code is what it is. Take the heat or get out of the kitchen as they say. The code was conceived under the requirements and restrictions of the day just as the code you write today is. Some day your code will also fall under the same scrutiny, if you're (un)lucky enough to have it survive that long, by somebody who will most probably not be you. Will that person share your beliefs and definition of maintainable code? I doubt it.

        Some day your code will also fall under the same scrutiny, if you're (un)lucky enough to have it survive that long, by somebody who will most probably not be you. Will that person share your beliefs and definition of maintainable code? I doubt it.

        At last. Realism from someone who has obviously been there, instead of book-learnt theoretic evangelism or purist dogma.

        Knowing enough to not put aspirations above requirements, does not mean you disregard maintainability. It means you achieve the best possible compromise between that aspiration and the hard requirements. If the requirements can be met with best possible maintainability, there is no conflict, but when there is, the requirement comes first. S'not rocket science.


        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.
        Will that person share your beliefs and definition of maintainable code? I doubt it.
        Given that my beliefs about the definition of maintainable perl largely agree with the defininitions in Modern Perl, PBP, and apparently even BrowserUKs definition, I doubt it less.

        Simplicity is a core practice of maintainability. Did someone say otherwise?



        - Boldra
      Your argument seems to support the claim that "bad things have been done in the name of maintainability"

      Hm. I don't think I wrote what you read. The gist of my post is that the simpler the code, the more maintainable it is.

      I get nervous when I see people suggesting that the initial design and implementation has to be done so as to accommodate future possibilities:

      Unless it is allowed for in the initial design and implementation you will pay far more to "retro-fit" maintainability

      The initial design (spec) should describe the actual, present day requirements. Nothing more.

      The initial implementation should implement that design and nothing more. No what-ifs, wouldn't-it-be-nice-ifs, or maybes. And absolutely no we-coulds.

      Anything beyond those actual requirements is an upfront cost that may never show return.

      It adds time to schedules--have you ever had one that wasn't too tight?

      It adds cost to the budget--have you ever had one that had a surplus?

      Complex code is always harder to maintain, no matter how well written it is. And writing complex code well always takes considerably longer and costs more.

      Think of it this way.

      If we could predict all future requirements and implement them perfectly up front, then maintenance would never be necessary. But it never works out that way.

      And the moment you accept that the original assessment of requirements will be shown to be wrong once the code goes live; that even when the requirements are correct, that bugs will make it into production; and that new (unimagined and unimaginable) requirements will always arise; the sooner it becomes clear that the earlier and more cheaply and more simply you get your first cut into real-world use, the sooner you will find out what bugs need correcting, and what changes arise, and the more likely it is that you'll be given a budget to achieve them. And the easier they will be to achieve.

      If you think now that one day you might need X; and you implement X, and later you do need X, then you've saved some maintenance budget down the road. But you've added to the costs at the time when they are most critical, when developing a new and unproven piece of code.

      Guess wrong; implement the wrong thing; and it is pure cost at both ends.

      I prioritise this way:

      1. Make it work.

        Self-explanatory.

      2. Do so on time & budget.

        If you overrun either, you may never make it into production. And if you do, you will be under severe scrutiny when you go looking for a maintenance budget.

      3. Make it work in a timely fashion.

        If your target response time is 3 seconds and you take 10. You've lost half your customers.

        If you delivery Friday's results on Sunday, you're too late.

        ...

      4. Make the codebase clean, flexible, reusable, and maintainable.

        These are aspirations, not requirements.


      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.
        Hm. I don't think I wrote what you read. The gist of my post is that the simpler the code, the more maintainable it is.
        If that was your gist, your node was poorly written.

        Utilitarian posted about the virtues of "allowing for" code to be fixed and modified, while 90% of your post is a rant about "adding functionality which is not required". However, thanks to your summary, I can see that there is actually not much we disagree on.

        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.