1. 19

  2. 8

    Just for context: Things You Should Never Do, Part I is the article that influenced my generation to prefer refactoring over rewriting.

    There can’t be a definitive answer to this sort of dilemma as it is inherently subjective: those who don’t feel comfortable at refactoring won’t be able to do it efficiently so their anecdotal evidence would tend to favor rewrites. And vice versa, I suppose.

    1. 10

      The biggest lie in that article is one of omission:

      The idea that new code is better than old is patently absurd. Old code has been used. It has been tested. Lots of bugs have been found, and they’ve been fixed. There’s nothing wrong with it.

      This makes the blatant assumption that the code has ever actually been tested, outside of perhaps some narrow context. From bitter experience, there are a lot of bits of code that are simply assumed to work and that have never been really stomped on, and even when by inspection you know that bugs exist (how large is this buffer? does this mutex get set correctly?) the folks who wrote it are often content just to leave it be out of some fear that it’ll break or that they’ll be made fun of (the mark of a truly immature developer is the fear of review).

      Indeed, the nasty truth not really spoken is that it is quite possible to run into a codebase that will never be testable in any meaningful fashion, and that you really are just better off talking to the business folks and giving them exactly what they need while the legacy stuff is quietly pulled out behind the chemical shed.

      1. 7

        It’s better if you pretend it says “battle tested” there, as in the code has been shipped and is doing whatever it does. It has survived at least minimal contact with the enemy.

      2. 0

        Why can’t there be a definite answer? I don’t like ‘there is never an answer’ because it suggests that something is not understandable, mythical or mystic, and I don’t believe that’s possible with software projects, and is permission for people to push opinion rather than objective fact. (If it is not understandable, then that might even be a basis on which to do a rewrite… ;-) )

        More productive to help people understand the tipping point at which rewrite is better than refactor with specifics.

        1. 2

          Why can’t there be a definite answer?

          Because there is no simple one-size-fits-all answer. Sometimes it’s better to refactor; other times it’s better to rebuild. Determining which one is preferable is a complex machine learning task (without a good training set available) at best, and such tasks always have to trade off erring on one side against erring on the other side; so “always refactor” and “always rebuild” are simplified strategies that are easily practicable.

      3. 3

        Hmmm. It didn’t sound so much like a rebuild. They were mostly adding new functionality on the side, no? The strangler is maybe the right way to go’s out this, but was there a plan to actually deprecate and replace the existing tangled mess? Or just hope it withered over time?

        My guess, based on the title, would be some system where you don’t try modifying existing code (refactoring) but you rewrite it with a lot of copy and paste. The existing code probably has a lot of functionality you want, it’s just wired up all wrong. So break it down into tiny bricks, then restack them appropriately.

        1. 1

          Wonderful article. I shared the bit about “every push goes to production” with my current team. We’re not there, but would love to be.

          1. [Comment removed by author]

            1. 6

              The point of testing is to capture behavior. You can’t, despite what a lot of functional people believe, waive a magic wand and sprinkle ML dust and suddenly have a system that completely meets business needs without some sort of QA work.

              You can build something that behaves as you programmed it, but you use to tests to verify that it behaves as you expect it to.

              If you are writing properly abstracted logic (be it through a function mapping one dataspace to another, or an object containing internal state and responding to messages), you must verify that the abstraction still holds when you change out its implementation. That is what testing is for.

              1. 5

                Functional testing is absolutely a requirement. But I do think that the sprinkle of ML dust can perhaps entirely replace unit testing.