Abstractions. Not the philosophical kind. The software kind. Interfaces. Adapters. Factories. Providers of providers.
We can debate the performance costs. Extra function calls. Virtual dispatch. Memory overhead. Those costs are real. But the cognitive cost is harder to see and often more expensive.
Every abstraction introduces rules. Concepts. Naming schemes. Failure modes. If it does not permanently remove more complexity than it adds, it increases entropy while pretending to create order.
I have seen codebases where flexibility was the north star. Everything was modular. Everything had an interface. In theory, any component could be swapped out.
In practice, nothing ever was.
But the layers stayed. The indirection stayed. The cognitive load stayed.
There is an asymmetry to abstraction. The person introducing it feels clever. The future maintainer inherits the mental overhead. And maintenance always outlives the sprint where the abstraction felt justified.
I try to remember that when I am tempted to introduce interfaceOfTheDay.
The value of an abstraction equals the complexity it permanently removes from everyday developer consciousness.
If I still have to think about the messy details every week, then the abstraction did not remove the mess. It just relocated it.