I'll give a concrete example where adding abstraction will make code shorter. This comes from my recent experience, with some details changed for the sake of discretion and simplicity.
Suppose you wanted to simplify the generation of contextual metadata for structured logging in an API service. The service handles requests that manipulate stored records, run logic, etc. Basic CRUD plus business logic.
The starting point is a bunch of raw calls to MDC.put() if a Java service, or an equivalent in another language[0].
An abstraction-free approach might give you a logUser method, a logUserAndAccount method, a logAccount method, a logTransaction method, a logTransactionAndAccount method, etc. This does at least simplify the actual request processing code from the starting point and make the logging consistent, but makes the program longer.
Alternatively, one could have a generic Loggable interface, with a function that returns metadata for the object to be logged, and a logWith method that takes a Loggable as a parameter. You can get fancy and provide a default implementation if all of your entities have common methods like id(). There are probably still ways to improve from here, but now instead of a dozen functions, you have one.
[0] Years ago I wrote a rubygem for this, but was not able to open source the bulk of it.
Suppose you wanted to simplify the generation of contextual metadata for structured logging in an API service. The service handles requests that manipulate stored records, run logic, etc. Basic CRUD plus business logic.
The starting point is a bunch of raw calls to MDC.put() if a Java service, or an equivalent in another language[0].
An abstraction-free approach might give you a logUser method, a logUserAndAccount method, a logAccount method, a logTransaction method, a logTransactionAndAccount method, etc. This does at least simplify the actual request processing code from the starting point and make the logging consistent, but makes the program longer.
Alternatively, one could have a generic Loggable interface, with a function that returns metadata for the object to be logged, and a logWith method that takes a Loggable as a parameter. You can get fancy and provide a default implementation if all of your entities have common methods like id(). There are probably still ways to improve from here, but now instead of a dozen functions, you have one.
[0] Years ago I wrote a rubygem for this, but was not able to open source the bulk of it.