Dan North gave a presentation a few years ago about some of the decisions a software architect or a developer might make.
At 46:50 he begins to talk about the DRY principle ("Don't Repeat Yourself"). He makes it clear that he's a fan of the Pragmatic Programmer, but this section of his talk is a warning about following one principle (DRY) without giving consideration to other competing principles. In this case, the opposite of DRY code is decoupled code, another desirable goal.
As much as we don't want to duplicate code, we also don't want to couple interfaces. Highly coupled code is fragile, slower to deploy (due to added complexity) and is at the heart of monolithic systems.
I've worked this year on several DRY and highly coupled systems and I would much prefer decoupled systems over DRY if I had to choose between them (but you don't... see below).
The problem isn't with DRY, per se. The problem is that people don't really understand what DRY means. It doesn't mean that you can't repeat a function or have a common algorithm in a few places.
The DRY (Don't Repeat Yourself) Principle states:
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
It's okay to have mechanical, textual duplication (the equivalent of caching values: a repeatable, automatic derivation of one source file from some meta-level description), as long as the authoritative source is well known.
For example, in C++ the interface and implementation for a class are typically specified in separate files, duplicating knowledge. You may consider the header file to be authoritative for the contract of the class as viewed by its clients, and the source code to be authoritative regarding issues of implementation which are hidden by the implementation.
While the duplication between .c and .h files is annoying, requires extra effort, and discourages the use of private member functions, it doesn't violate DRY because the compiler enforces that the function definition in the .c file must match the prototype in the .h file.
The principle also assumes that your projects have a high degree of automation, allowing the generation of the derivative knowledge artifacts whenever required.
Correct use of DRY means that you can make a change to an element of a system and logically unrelated parts do not need to be changed. Highly coupled systems—the incorrect use of DRY—means that every time you make a change to an element, you need to find all of the affected parts and fix them.1
Writing DRY code before you've spent the time learning what parts should be DRY leads to premature generalization: you generalize and make abstractions in the wrong places, creating highly coupled code and making refactoring difficult.