My Principle of Applying Principles:
Principles, patterns, and practices are not final purposes. The good and proper application of each is therefore inspired and constrained by a superior, more final purpose.
You need to understand why you’re doing what you’re doing!
(The POAP is not exempt from the POAP.)
In other words, the default position should be not to apply a principle unless you understand reasonably well why you're applying it.1 Blind faith in anything tends to go wrong; software engineering principles are no exception. Mindless adherence is superstition. It generally just makes extra work and can produce results antithetical to the very intent of the principle. (And that's if it's even a good principle!)
So, I'm always delighted to see questions like this that make an effort to understand a principle's purposes.
My Suggestion
If you don't already have your head wrapped around the principles and their purposes, just create a cheat sheet for yourself to remind yourself what each principle should accomplish, how it might go wrong, and add any special notes for your particular software/business domain.
Something like this:
Principle |
Purpose |
Pitfalls |
Domain Specific Notes |
Single responsibility |
Makes changes isolated, easy to test and reason about. |
Hyper-decomposition. You probably don't need a full class to set a page/screen title. |
This is your homework. |
Open–closed |
Help prevent new features from breaking old ones. |
"Closing" a module you own, maintain, and can safely extend can lead to unnecessary complexity, inheritance chains, LOC, and added binary/bundle size. |
This is your homework. |
Liskov substitution |
Helps prevent code that looks correct from being incorrect. |
Over-reliance on LSP as a signal for correctness. |
This is your homework. |
Interface segregation |
Allows variable/function to accept to more types; Reduces boilerplate for using functions. |
Requiring the smallest possible interface may conflict with intent of LSP. |
This is your homework. |
Dependency inversion |
Makes more of the system reusable, swappable, and testable. |
Over-inversion. Creating and "inverting" abstractions with no gain, adding complexity, making code harder to read and reason about. |
This is your homework. |
Bonus: Don't Repeat Yourself |
Helps prevent inconsistent application of business rules throughout the system. |
Over-application: Code that looks the same might serve different purposes and behavior that should diverge become difficult to change. |
This is your homework. |
My entries above are quick and dirty, and you're free and encouraged to disagree about the purposes and pitfalls. You should really research and reflect on each specific principle for yourself and how it applies to your particular system + domain.
Let me reiterate: How each of these applies specifically to a domain or architecture really depends on things only a domain expert would know -- e.g., What in this system is likely to change? What types of logic will be cross-cutting? Etc. So, do some thinking! Take notes! And share them with your team.
1. This answer generally assumes you don't have a tech lead to defer to or that you are the tech lead — and in either case are trying to make a judgement call. But, if you're reading this as a junior engineer or you're new to a team or product, etc., "a senior engineer told me to apply the principle in some way and I trust them [enough]" is a sane enough (though temporary) reason to just apply the principle. (While you grow into your role and take more ownership.)
Remember, even the POAP is subject to the POAP! And the point of the POAP is to keep you from doing silly, wasteful, or harmful things for the sake of a principle! (Or pattern. Or practice.) The POAP is not exempt! Fighting about principles is usually a violation of both the POAP's intent and probably the intent of the principle you'd be arguing about.