It's all a bit no-true-Scotsman though.
abstraction != dry
bad abstraction != anti-dry
Abstraction is primarily about separation of concerns, not about avoiding repetition. Drying out code that's repeated all over isn't the same as creating a formal abstraction for some element of the overall logic.
Which is why
>once a codebase uses several very wrong abstractions, it becomes significantly more confusing to work on, exponentially increasing cost.
And drying out code makes it easier to maintain, but it doesn't guarantee that the architecture isn't a mess.
The problem is perhaps that CS teaches algos, and sometimes it teaches design patterns. But there's almost no useful theory of abstraction design.
Design patterns are more or less as good as it gets, and all they do is give give you a cookbook of stock formulas to try.
Beyond that, there's no useful way to reason about abstractions, test them for domain fit, or rate them for elegance and efficiency.