Bad code is still bad code. And correctly abstracting everything is still the ideal. Abstractions are like hinges. Too few and everything is a big fused mess. Too many and your system is a wobbly mess you can get lost in. You need the right abstractions in the right places.
For my money the way to do that is to start with as few abstractions as possible and only add them in the places you need them, when you need them. Start simple and refactor as you go. If you try to predict where the abstractions go before you’ve designed your system, you’ll get it wrong and then you’ll either leave your code with bad abstractions or spend far longer than you had to refactoring. (Since refactoring is harder the more code you have).
For my money the way to do that is to start with as few abstractions as possible and only add them in the places you need them, when you need them. Start simple and refactor as you go. If you try to predict where the abstractions go before you’ve designed your system, you’ll get it wrong and then you’ll either leave your code with bad abstractions or spend far longer than you had to refactoring. (Since refactoring is harder the more code you have).