I see DRY as a smell, not a principle. If you see clones (same code in multiple places), then it's likely indicating that there is something that can be factorized. Now the question you should ask yourself before factorization is whether the duplication is coincidental (as the author shows) or if it's because the logic was copy pasted. Most of the time it's the second case, and duplicate code does make maintenance harder and riskier.
One of my pet-peeves is clones in unit-tests. People tend to care less about code quality when it comes to unit-tests, and code gets copy-pasted all over the place. The result is usually an unmaintainable ball of mess, where the most subtle variation in the unit being tested requires you to apply the same change in 15 different places. In this situation, DRY is a very useful indicator that something is going wrong.
Now the opposite of DRY is YSHRY - You Should Have Repeated Yourself. When you start adding 5 boolean parameters to a function to adapt it to all its calls, it's a smell that you thought you should have DRY, whereas YSHRY.
In my case when I was junior I tried to be very smart and try to DRY a lot, but I found that most of the times is better to write "dumb" code and repeat yourself if the complexity is not worth, also as you stated if your function is used in a lot of places for slightly different things is just so easy to break something without noticing and also harder to test.
So I agree with you, as developer you should know when do duplicate code and when DRY, but overall try to maintain your code as simple as possible, that makes also easier to maintain.
Only experience teaches you where to apply DRY and where not to.
Sometimes just because something looks the same or is similar does not mean it’s the same. Applying DRY just because it looks the same can have the unwanted consequence of changing in 1 place changing in another too when that’s not the desired affect. Then you add another parameter and conditional logic just because you don’t want 2 similar looking things.
Out of all the comments on this post, this is the one I agree with the most. I have been bitten by "not enough DRY" as well as "too much DRY".
I think experience as well as the maturity of the code are most relevant when deciding things like this. If you do this too early you will back yourself into a corner or eventually end up with a "god function" (usually the outcome of what OP mentions about conditional logic and more params).
If you wait too late you will inevitably have a giant codebase with lots of duplicates everywhere.
In my experience it's easier to clean up duplicated code than it is to break apart a "master function" that nobody has touched in 3 years out of fear.
One of my pet-peeves is clones in unit-tests. People tend to care less about code quality when it comes to unit-tests, and code gets copy-pasted all over the place. The result is usually an unmaintainable ball of mess, where the most subtle variation in the unit being tested requires you to apply the same change in 15 different places. In this situation, DRY is a very useful indicator that something is going wrong.
Now the opposite of DRY is YSHRY - You Should Have Repeated Yourself. When you start adding 5 boolean parameters to a function to adapt it to all its calls, it's a smell that you thought you should have DRY, whereas YSHRY.