I’ve watched DRY get applied as a reflex more than as a rule. Two similar lines appear and a helper is extracted, and six months later that helper is a flag for every caller and a name that no longer covers any of them. The failure isn’t that the team deduplicated. It’s that they deduplicated on appearance and skipped the second sentence Hunt and Thomas wrote, the one with the word knowledge in it.

Code that looks alike isn’t necessarily the same fact. Two loops that happen to iterate the same way for different reasons are two facts. Merge them and the next change to one becomes a change to both, which is exactly the rot DRY exists to stop. The test I use is whether the two copies would have to change together. If they would, they’re one fact. If they wouldn’t, they’re two facts that currently rhyme.

The boundary matters as much as the count. Inside a bounded context, deduplication is cheap insurance, because both copies answer to the same owner and the same change. Across a context boundary, a shared helper fuses two products into a single change, and now every team that touches one has to think about the other. The coupling costs more than the duplication did.

So I extract on the second occurrence inside a context. A little copied code is cheaper than a helper with a name that means three things.

Fair to the reflex: the reflex exists because duplicated knowledge really does rot, and neglected copies drift until there’s no way to tell which is right. The rule is sound. The trigger is wrong.

The full write-up is at https://prickles.org/tenet/dont-repeat-yourself/F3

  • Riskable@programming.dev
    link
    fedilink
    English
    arrow-up
    2
    ·
    edit-2
    2 days ago

    As time goes on I feel like the concept of DRY has more applicability to compiled languages like C/C++ and Rust than interpreted ones.

    From a concise coding standpoint, it’s a great idea most of the time. However—having worked with some long-term projects—I’ve found that it can create “code smells” if it’s taken a little too seriously.

    For example, I’ll often see loads of tiny (Python) functions that do simple things like applying a regex. Then I’ll search the code and see that a single regex function gets called in just two places.

    If that was the only time it happened, it wouldn’t be a big deal. Except I’ll see dozens of tiny functions like this at the top of many modules and be scratching my head wondering why they went to such an extreme. It makes my job as a reviewer harder because now I have to scroll all the way to the top of the script and back constantly to figure out WTF it’s doing.

    Not only that, but Python doesn’t have a JIT that would optimize away those extra functions calls. Every function call in Python carries some overhead (both CPU and memory), so by dividing up your functions into dozens of little ones you’re actually slowing it down.

    Normally such slowdowns are so trivial it doesn’t matter. But when someone is following DRY like some kind of religious extremist, those extra calls really can and do add up. Especially when you find them being called inside of deeply nested loops!