For a living codebase, nowadays my general rule of thumb is to consciusly duplicate code until it covers 3 different cases, and only then refactor (unless the DRY way is as fast and obvious).
It takes more than that to yield spaghetti and a lot of time is saved on premature generalization. Plus the generalization is often way more straightforward once the explicit cases are already implemented.
For a living codebase, nowadays my general rule of thumb is to consciusly duplicate code until it covers 3 different cases, and only then refactor (unless the DRY way is as fast and obvious).
It takes more than that to yield spaghetti and a lot of time is saved on premature generalization. Plus the generalization is often way more straightforward once the explicit cases are already implemented.