Earlier quoted context omitted.
That sounds like myriads of programmers have done it wrong for decades. Can you elaborate on this and maybe add a few sources?
If you have to change in 5 places to change one "thing", the code is probably not DRY.
drawRectangle("blue", x0, y0, x1, y1);
Do you refactor them into drawBlueRectangle(x0, y0, x1, y1)?It seems you removed the duplication but you didn't. Because if you now have the requirement to draw red rectangles instead you surely won't leave it as
def drawBlueRectangle(x0, y0, x1, y1):
drawRectangle("red", x0, y0, x1, y1)
So instead of changing 5 places you now have to change the invocation in 5 places and implementation in 1 place.You can argue it's because you named it wrongly, it should be "drawWhateverThingTheyHaveInCommonRectangle" instead, for example drawHighlightingRectangle. And you'll be right.
But you don't know if they will have that thing in common forever. And splitting the code is harder than refactoring it, so it often leads to code like this:
def drawHighlightingRectangle(someDecidingFactor1, someDecidingFactor2, x0, y0, x1, y1):
if someDecidingFactor == something && someDecidingFactor2 != somethingElse:
drawRectangle("red", x0, y0, x1, y1)
elif someDecidingFactor2 == somethingElse:
drawRectangle("blue", x0, y0, x1, y1)
This code may seems ok, but it tends to grow business logic inside, and you don't immediately know what combinations of deciding factors are actually possible without looking at all the invocations. So either you look at all the invocations before implementing your change (and then the refactor didn't actually save you any work - what does it matter if you look at code and change it vs just look at code and change stuff elsewhere), or you ignore the invocations and add your change in isolation (probably writing code that is redundant and overcomplicated because you handle cases that cannot happen).This is obviously oversimplified example, but I've done these exact mistakes several times :)