Counter: Every failed IT project that I have worked on in the last 20 years had too much code. Code is bad. Delete code mercilessly.
Seriously though, the problem are bad abstractions, not just abstractions. A total lack of abstractions is typically a spaghetti you need to read fully to understand.
When I read these threads I feel like I must be working on another planet to the people commenting in them.
In almost every front-end project I join, there's a positive correlation between number of abstractions and code size. Everything is so "best practice one size fits all"-y that you can usually start with halving the size of the code base by removing 50% of the dependencies.
Even once you've done that, you can usually speed up development by cutting 50% of the remaining dependencies. All the ones where the API surface area is more complex than the bloody implementation. Code is bad, sure, but at least it speaks for itself. A lot of the time the choice is between 3 lines of code that are well written and self explanatory, or 1 line of code that is incomprehensible without trawling through 800 lines of documentation first.
I agree that code size is probably the most important metric for measuring complexity, but it's not an absolute thing. If you're too merciless with culling code, you can easily code yourself into a shitstorm of required context that makes hiring and onboarding impossible. Having said that, I think the problem is mostly contained to using other people's abstractions. I can't think of many times I've walked onto a project and gotten lost in the mud because someone there had coded up something that was too convoluted. Only one springs to mind and it was a back-end system.
And that probably gives some clue as to why nobody can agree on this stuff. I'm guessing different ecosystems lie on different points on the scale, and so different approaches are going to be more successful. I've seen probably 20 projects grind to a halt because of overabstraction, and none because of code duplication. But I'm sure there's other programmers involved in other communities and industries that have seen the opposite. So if we're talking to a faceless crowd on a forum, we're going to give vastly different advice, under the assumption that the people we're talking to are somewhere around the average of everyone we've ever worked with.
I think that it depends on what you define as an abstraction. I think we're often just counting wrong or plainly heavy-handed abstractions here.
There are many abstractions which you can cut and reduce code size, e.g.:
* Complex frameworks which do not fit your case
* Overused GoF-style design patterns which have no place in this day and age
* Magic ORMs generated with annotation processors
* Universal Tool Factory Factory Factories[1]
The thing is, you're usually not replacing these abstraction with plain old code duplication (let alone the dreaded "fixed A here, fixed B there" copypasta). You usually replaced dependencies (frameworks, ill-fitted libraries and factory-factory-factories) with your own implementation which is a better fit for your needs, and that can be viewed as duplication - sure. But you'd usually still only have ONE implementation in the code base.
In short, in most cases I've seen where we eliminated bad abstractions and save on code, we replaced them with good abstractions, not (a lot of) duplication.
> there's a positive correlation between number of abstractions and code size
What really matters to me is how much code I need to read to understand how it works. I prefer to work in codebases where I need to read 100 lines out of 1200 instead of 1000 of 1000.
Good abstractions are not about code duplication - they are about simplifying conceptual models - so I don't need to understand these details of everything, but I can still understand the system as a whole. And this is the hard part to get it right.
Many developers often mistake indirection for abstraction. The worst codebases I worked on had plenty of indirection, many components and many dependencies (often cyclic!), but they actually lacked abstraction or mixed different abstraction levels. A component that is so universal that it can render webpages, solve differential equations and wash the dishes all in one function is not a good example of abstraction.
Seriously though, the problem are bad abstractions, not just abstractions. A total lack of abstractions is typically a spaghetti you need to read fully to understand.