Abstraction
An abstraction hides detail behind a useful model, and charges indirection, vocabulary and leakage for it. When to pay, and when duplication is cheaper.
A useful model that lets a caller ignore something specific. `PaymentGateway.charge()` hides provider HTTP; `UtilityManagerFactoryHelper` hides nothing and is therefore not an abstraction at all.
Indirection, vocabulary, learning cost and leakage — charged to every future reader, forever, whether or not the flexibility is ever used. Do not abstract by default.
A heuristic about evidence, not a counting rule. Duplicate until the pattern is a pattern, because the third case is usually the first one that shows you which parts actually vary.
`Repository.save()` claims database independence while transaction scope, isolation level, index behaviour and failure modes come straight through. An abstraction hides a mechanism; it cannot erase the physics underneath it.
The wrong abstraction costs more than the duplication it replaced, because duplication is visible and a wrong shared unit is not. Its signature is callers that diverged and a parameter list that grew flags to hold them together.
An abstraction buys flexibility along one axis and pays along every other. Which axis you pick is not a detail of the design — it is the design.