The SOLID principles for software design can be a helpful thinking and communication tool of a software developer, even if perhaps just as a reason to think about software design from another perspective. While there are many more software design principles that are arguably at least as important, the SOLID principles may give some additional guidance. Unfortunately, like many such “principles” and catch phrases, they are commonly misunderstood or applied without context and the result can be even worse than not being aware of them in the first place and just going with your gut. There’s a lot of valid criticism of the SOLID principles even when understood as intended, but in this article I want to focus on the (arguably worse) misinterpretations. I might cover valid criticisms of the SOLID principles in another post some time.
Before we go into the specifics of how I have seen them misunderstood many times, first some general advice: While most of these principles are not super complicated, they are also not completely trivial, and any summary of the 5 principles without deeper context and the intention behind them is almost guaranteed to be misunderstood. This is why I will not try to explain the principles in detail in this article and instead focus on common misunderstandings, perhaps providing some additional thoughts in some cases. I think we should talk more about myths and misconceptions in software development in general, because education and useful information always stands against an ever-growing current of (well-intended) misinformation, especially on the web. Often they are simply summarized with 1-2 sentences per principle in blog posts or social media posts, and I think it would be more effective to just say “the SOLID principles exist, go read up on them and form an opinion” instead of implying that reading 2 sentences and an over-simplified example out of context is learning.
I strongly urge you to read up on the principles, ideally using multiple including the original sources, and explicitly searching for deeper discussions and common misconceptions as well as criticism and alternatives. Basically, engage in double-loop learning: Not just memorize the 5 principles, but also learn why they exist, how you will probably misinterpret them and thus how you can avoid it, and whether there are alternatives that are easier to understand, less likely to misunderstand, and possibly even more valid in today’s context. Assume that your first instinct (if you’re hearing about SOLID for the first time) or your current understanding of the SOLID principles (if you’ve already been familiar with them) is incorrect, and try to disprove that assumption. This applies doubly so when you are a content creator who intends to spread awareness and information about these principles, because too often the same myths and misconceptions are just reiterated in blogs and articles basically copying from each other. When we spread information, we have a responsibility to do our due diligence to at least try not to inadvertently spread misinformation. And above all, while we’re fixing or at least questioning our current understanding of those principles, remember that they can’t free us from the necessity to be intentional and think about what we’re doing. The map is not the terrain, and from time to time we need to look around and see where we ended up.
SRP: Single Responsibility Principle
This is probably the most misunderstood and misused one. Common misconceptions are often formulated similar to the following, which might in some contexts still be valid, but have nothing to do with the SRP and can be detrimental to the design of our code depending on the interpretation.
- “A class should only have one job/only perform one task.”
- “A function/method should only do one thing.”
- “It also applies to functions.”
- “A larger single responsibility can be divided into many smaller and even singler responsibilities.”
The fact that the SRP tries to increase cohesion in addition to achieving looser coupling is often completely disregarded. “Group together things that change together.” After all, we usually also don’t want a single change of functionality to affect 20 different modules. Turns out, the original author who coined the principle even said that he regrets the name because of how easily it is misinterpreted. “Responsibility” is intended to mean “reason to change”, and since even that is misleading, it has been clarified since by the original author Robert C. Martin to mean (paraphrased) one source of changes.
Wrong interpretations based on the name or a wrong/oversimplified explanation understandably cause some people to reject and criticize the SRP, but for the wrong reasons. Criticism of the principle based on the correct understanding and within context can of course still be valid. The way we develop software has changed quite a bit in the last decades. For example, multiple developers changing the same class while working on different features may not be ideal, but with today’s tooling (like distributed VCS and IDE-supported refactoring) and good practices (like continuous integration and automated testing) it’s not as problematic as back when only one developer could be working on a file at a time.
Further reading
- https://thevaluable.dev/single-responsibility-principle-revisited/
- https://www.sicpers.info/2023/10/ive-vastly-misunderstood-the-single-responsibility-principle/
- https://galiarmero.dev/blog/why-the-single-responsibility-principle-is-often-misinterpreted/
- https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html
- https://blog.cleancoder.com/uncle-bob/2012/02/01/Service-Oriented-Agony.html
- https://softwareengineering.stackexchange.com/a/307785/195641
OCP: Open-Closed Principle
Common misconceptions and oversimplifications include:
- “We need to replace all switch-statements with polymorphism.”
- “Once a class has been implemented, its behavior should be changeable without having to change that class.”
- “We should make all classes virtual so that their behavior can be extended through inheritance.”
Often disregarded is the important concept of strategic closure, which you will find for example in the paper by Robert C. Martin from 1996 (originally it was coined even earlier by Bertrand Meyer in 1988, but refined by Martin). That means, we deliberately close against certain kinds of changes, strategically rather than speculatively making specific changes easy. In practice, plugin architectures are maybe the best example where we deliberately make certain parts of the application extensible without requiring changes to other parts, because the people who implement the plugins are not the same as those implementing the pluggable interface. However, not everything has to be a plugin, especially within the same application (or even the same component) maintained by the same people. It seems more like a design pattern for certain situations rather than a principle to apply in most contexts. Outside of those specific situations, similarly to avoiding multiple developers changing the same module for different reasons in case of the SRP, changing existing code in general is a whole different story nowadays with refactoring tools, faster compilation, and similar advancements, which might affect what we consider “flexible” and “easy to change” code compared to the 80s and 90s. Let’s remember that the OCP predates the “Refactoring” book, XP, TDD, as well as many more software design principles, books, and advancements.
In his original paper, Robert C. Martin built on Meyer’s work and defined the principle quite strictly. In later books, Martin rephrased it multiple times, either because he learned that it was not accurate anymore, or that it was being misinterpreted or taken too literally. In accordance with the concept of strategic closure, he describes the principle in later iterations as “You should be able to change the environment surrounding a module without changing the module itself”, which sounds pretty reasonable and less dogmatic to me. It also does not imply any specific technical mechanisms like inheritance. In a blog post he later says “Ideally, you will be able to add the new behavior by adding new code, and changing little or no old code.”, which is quite different and more reasonable than talking about not allowing to change the source code of existing modules. In another blog post Martin argues that the existence/usefulness of plugin architectures proves that the OCP is true, but I’d counter that it only proves that the OCP applies to certain situations, and it is more or less retrofitted to do so. When teaching or sharing knowledge about the OCP, we arguably should not blindly copy the old definitions that are already refuted even by the author himself. And if the author redefines the principle as he learns about its shortcomings but people still misguidedly swear by an outdated version of the definition out of context, maybe we should look for a new (or other existing) principle as an alternative rather than keeping the name for its popularity alone and blurring its semantics.
Further reading
- https://blog.cleancoder.com/uncle-bob/2013/03/08/AnOpenAndClosedCase.html
- https://blog.cleancoder.com/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html
LSP: Liskov Substitution Principle
Misconceptions of the LSP that I’ve heard include:
- “We should use inheritance.”
- “All subtypes of an abstract class or interface must implement all of its abstract method declarations.” (duh!)
- “In real life, a square is a special kind of rectangle, but in the programming world it’s the other way around.”
- “A subtype must behave like its supertype.”
ISP: Interface Segregation Principle
Probably the one talked about the least, at the moment I can’t really think of any misconceptions that I’ve frequently heard about the ISP. It’s probably the simplest and least ambiguous of the five principles. There might be quite a bit of overlap with misunderstandings of DIP and LSP, though, since the concept of abstraction is often reduced to “define an interface”.
DIP: Dependency Inversion Principle
- “Dependency Inversion is the same as Inverson of Control.”
- “Dependency Inversion is the same as Dependency Injection.”
- “Instead of a class
Frobulatorwe should have an interfaceFrobulatorand a classFrobulatorImpl.”
While dependency inversion and dependency injection sound similar, they are not the same and can even be realized independently. Similarly, inversion of control (IoC) is related but not the same concept.
I often see dependency inversion (or the dependency inversion principle) misapplied when developers try to implement, for example, “clean architecture” in their project. They copy the folder structure from some blog post or template project and introduce interfaces in a way that looks the same as described in the book or blog posts about clean architecture, but without actually inverting the dependencies. Every piece of the implementation’s public API is part of the interface and the interface is often owned by the implementing class rather than by the caller, which results in indirection without abstraction. Rather than depending on the interface and not the implementation details, the caller now depends on both, in addition to the dependency being bidirectional.

