Framework vs Application Code for Managing Complexity
A central skill in programming is splitting problems into adequate abstraction boundaries.
What disciplines such as Operating Systems and Compilers teach, besides the specifics, is how programmers have tackled these very complex problems: finding the proper abstraction boundaries, splitting the complex system into a collection of simpler problems, solving each of the simpler problems, then bringing all the parts together into the whole solution.
After all, programming is mostly about managing complexity. As systems grow complex enough, the programmer can't hold it all in their head.
Pushing changes to software becomes painful and complex when the programmer can no longer be sure what the boundaries of the affected code are.
This is where a useful distinction can be drawn: framework code versus application code.
Framework code is the foundation, the core on which the application code is built.
Framework code solves recurring problems which are not application-specific: accessing the database, validating the input parameters, logging the errors, returning the properly encoded response, and so on.
Because many other pieces of the software depend upon the framework code, it's a good practice to keep it stable, small, simple, and well-tested.
When it's not clear that a piece of application code must be “promoted” to the core, wait for two or three instances where it recurs.
The second part is the application code. This is closer to “data” than it is to “code”. Application code has achieved its goal when it solves a business problem or serves an end-user scenario.
The framework code never depends on the application code. The problem the framework is set out to solve is application-agnostic. It serves as an interface from some higher-level concepts to the nitty-gritty implementation.
The application code ideally depends only on the framework code, not on other application code. Having an unclear boundary of which application code depends on what is the surest way of producing unintended consequences when the most innocuous thing is changed.