The perspective you have can be skewed by personal experience. This is a slippery slope of facts that are individually correct, but the resulting inference isn't, even though it looks like correct at first glance.
- Frameworks are larger in scope than small projects.
- Bad practice is significantly harder to deal with in larger codebases.
- Building a framework (on average) requires a more skilled developer than building a small project.
- Better developers follow good practice (SOLID) more.
- As a result, frameworks have a higher need for good practice and tend to be built by developers who are more closely experienced with good practice.
This means that when you interact with frameworks and smaller libraries, the good practice code you'll interact with will more commonly be found in the bigger frameworks.
This fallacy is very common, e.g every doctor I've been treated by was arrogant. Therefore I conclude that all doctors are arrogant. These fallacies always suffer from making a blanket inference based on personal experiences.
In your case, it's possible that you've predominantly experienced good practice in larger frameworks and not in smaller libraries. Your personal observation isn't wrong, but it's anecdotal evidence and not universally applicable.
2 modes of programming - creating more or less exactly what is asked via requirements and KISS (typical programming), or creating very generic and reusable logic, services, etc that provide the flexibility other developers may need (framework programming)
You're somewhat confirming this here. Think of what a framework is. It is not an application. It's a generalized "template" that others can use to make all sorts of application. Logically, that means that a framework is built in much more abstracted logic in order to be useable by everyone.
Framework builders are incapable of taking shortcuts, because they don't even know what the requirements of the subsequent applications are. Building a framework inherently incentivizes them to make their code usable for others.
Application builders, however, have the ability to compromise on logical efficiency because they are focused on delivering a product. Their main goal is not the workings of the code but rather the experience of the user.
For a framework, the end user is another developer, who will be interacting with your code. The quality of your code matters to your end user.
For an application, the end user is a non-developer, who won't be interacting with your code. The quality of your code is of no importance to them.
This is exactly why the architects of a development team often act as the enforcers of good practice. They are one step removed from delivering the product, which means they tend to look at the code objectively, rather than focusing on the delivery of the application itself.
If you do add these entry points of abstraction, are you really fulfilling the users requirements, or are you creating a framework on top of your existing framework and tech stack to make future additions easier? In which case are you serving the interests of the customer, or of the developer?
This is an interesting point, and it's (in my experience) the main reason why people still try to justify avoiding good practice.
To summarize the below points: Skipping good practice can only be justified if your requirements (as currently known) are immutable, and there will never be any change/addition to the codebase. Spoiler alert: That is rarely ever the case.
For example, when I write a 5 minute console application to process a particular file, I don't use good practice. Because I'm only going to use the application today, and it doesn't need to be updated in the future (it'd be easier to write a different application should I need one again).
Let's say you can shoddily build an application in 4 weeks, and you can properly build it in 6 weeks. At first sight, shoddily building it seems better. The customer gets their application quicker, and the company has to spend less time on developer wages. Win/win, right?
However, this is a decision made without thinking ahead. Because of the quality of the codebase, making a major change to the shoddily built one will take 2 weeks, while making the same changes to the properly built one takes 1 week. There may be many of these changes coming up in the future.
Furthermore, there is a tendency for changes to unexpectedly require more work than you initially thought in shoddily built codebases, thus likely pushing your development time to three weeks instead of two.
And then there's also the tendency to waste time hunting for bugs. This is often the case in projects where logging has been ignored due to time constraints or sheer unwillingness to implement it because you absentmindedly work under the assumption that the end product will work as expected.
It doesn't even need to be a major update. At my current employer, I've seen several projects that were built quick and dirty, and when the tiniest bug/change needed to be made due to a miscommunication in the requirements, that lead to a chain reaction of needing to refactor module after module. Some of these projects ended up collapsing (and leaving behind an unmaintainable mess) before they even released their first version.
Shortcut decisions (quick and dirty programming) are only beneficial if you can conclusively guarantee that the requirements are exactly correct and will never need to change. In my experience, I've never come across a project where that is true.
Investing the extra time in good practice is investing in the future. Future bugs and changes will be so much easier when the existing codebase is built on good practice. It will already be paying dividends after only two or three changes are made.