When I first heard about the SOLID principles as a young and innocent developer, many of them seemed confusing to me. The Open-Closed Principle looked easy, however: we should extend the behavior of our classes instead of modifying them.
I did extend the behavior of my classes indeed! Mainly by using inheritance in one form or another. The end result? An abstracted mess, where everybody was wondering what was the use cases my code was covering, hidden under a cheer amount of abstractions and indirection.
What did I get wrong? Did I really understand the Open-Closed Principle? I still see many developers speaking about this concept in confusing ways, so let’s explore it in this article. More precisely, we’ll see:
- What developers usually mean when they speak about the Open-Closed Principle.
- The origins of the Open-Closed principle.
- Robert Martin’s version of the Open-Closed Principle.
- The Problems of the Open-Closed principle.
To save my fingers from some painful typing, and because we all love acronyms in software development, I’ll use “OCP” for Open-Closed Principle in this article.
This is it: are you ready to be open and closed at the same time? I’m not, but let’s do it anyway.
The Holy Definition
When the OCP is brought up in enlightening conversations, everybody think of it as a part of the holy SOLID principles gathered by Robert Martin. The SOLID acronym presumably gather useful “principles”, ideas which are “paraphrased” from influential authors or heavily inspired by some books or research papers.
So, what’s the OCP? Martin first defined it when dinosaurs were ruling the Earth, in 1996:
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification
Let’s imagine that your “software entity” is the class “Shipment”. Your project manager asks you to change the behavior of the shipments in your disruptive application. Should you modify the class?
Absolutely not! Your class should be close to modification. We don’t have the right to change it. But how can we ease the wrath of our project manager? We need to extend our class!
This is a weird idea. Have you ever seen a developer doing her job without modifying the code, only adding to it? I didn’t. But it’s possible that I’ve only worked with pagans who didn’t know the Real Truth©. I worked with people trying to implement the OCP too, but they still had to modify some code at some point.
But let’s do some more research before digging the grave of our good old OCP. Where does this principle come from?
The Origin of the Open-Closed Principle
At the beginning of Martin’s seminal article about the OCP, it is said that its definition is a “paraphrase” of an older idea from Bertrand Meyers. He’s indeed the one who coined the terms “Open-Closed Principle” in his book Object-Oriented Software Construction, written when some cells were beginning to clone themselves in vast oceans, in 1988.
So, let’s look at this book! A disclaimer though: I didn’t read it entirely (I couldn’t swallow the 1200 pages), but I had the courage and the resilience to read large chunks of it. Some old books about software development are still good nowadays, it’s not one of them.
The advice given here are generally bad. That said, it’s unfair to judge a 22 years old book with the actual standards, so let’s just say that it’s a product of its time.
Martin, even if he’s trying to “paraphrase” Meyer’s old idea, doesn’t get it right. Meyer doesn’t speak about “software entities”, he’s speaking only about classes:
Classes should be the only modules.
Meyer is also the creator of the Eiffel programming language (1996). This was an important language, later influencing the design of Java and Objective-C. Eiffel was strongly object-oriented. You can clearly see it in the book, Meyer is a big fan of OOP and inheritance. In his own words:
Classes will exchange information exclusively through feature calls, and through the inheritance mechanism.
He’s speaking about inheritance of implementation here. That’s not all: he’s also advocating for multiple inheritance, a practice which is very difficult to get right in practice:
It should be possible for a class to inherit from as many others as necessary, with an adequate mechanism for disambiguating name clashes.
Why so much emphasis on inheritance? Why not using good old composition? The mantra “composition over inheritance” didn’t exist yet; it came in 1994 in the book Design Pattern, years after the publication of Meyer’s book.
For Meyer, inheritance should be used as a polymorphism mechanism, to reuse code (one of the main concern of the book), and, of course, to respect the OCP. That’s the main reason why Meyer’s book didn’t age well: since then, we saw the limits of traditional inheritance and the will from modern language to get away from it as much as possible, creating more focused constructs like interfaces or traits in the process.
Even if Martin says that he’s paraphrasing Meyer, their definitions of the OCP are different. It’s not only about the definition, it’s about how to respect the “principle”.
Robert Martin’s Open-Closed Principle
In his seminal article, Martin wrote that we should not change existing code but always adding code:
When requirements change, you extend the behavior of such modules by adding new code, not by changing old code that already works.
He’s going very far in his argumentation, even saying that the “old” code should be “inviolate”. For him, to follow the OCP way of life, we should create new abstractions instead of modifying new code. Concretely, he’s advocating, like Meyer, to use inheritance of implementation, but not only: we should also use abstract classes and interface constructs to respect the OCP.
In another article, Martin introduces the concept of “plugins architecture”:
Plugin systems are the ultimate consummation, the apotheosis, of the Open-Closed Principle.
For him, many successful systems like “Vim, Emacs, Minecraft, or Eclipse” have this plugin systems, proof that the OCP is something we should all follow.
Great! So, what’s the problem then?
The Problems of the Open-Closed Principle
Now that we saw the origins of OCP as well as Martin’s own definition, we’re finally ready to determine if the OCP is worth following.
What About Refactoring?
Let’s address the elephant in the room: refactoring. To add a new functionality to an existing codebase, we often need to modify the codebase itself, for two reasons:
- For the new functionality to work with the old ones.
- To keep the quality of the code high.
Now, instead of refactoring, let’s imagine that we create new abstractions each time. It means that we go around some deprecated behaviors by adding some code. In my world, it’s called a hack, the art of going around old code, letting it pollutes the codebase, and the mind of the poor developers. This is the Highway to Hell, the Path to Confusion, the Explosion of Line of Code. Don’t do this at home.
Creating abstraction each time we want to modify our code bring other problems: it can create indirections and, as a result, increase the complexity of the system. If we try to create each time a new “entity” (using Martin’s words) when we want to change one, we’ll see the cheer numbers of “entities” going through the roof.
If you look at the last example of Martin’s article, the code is ridiculously complex for such a small functionality. New abstractions should only be created when we have concrete reasons to use them, not each time we want to change our code.
The other examples Martin gives are pretty generic and, therefore, pretty bad. He’s speaking about
Circles, and other classes which have nothing to do with the reality of software development. It’s easy to use any form of inheritance for concepts which don’t change. But when we work for some organizations, we have to deal with domain concepts linked to the messy and ever-changing world. A circle stays a circle, but what about a
Shipment class? A
User? As I already wrote, inheritance of implementation for domain concepts is generally a bad idea.
For Martin, as we saw above, the “plugin architecture” is the apotheosis of the OCP:
What if the design of your systems was based around plugins, like Vim, Emacs, Minecraft, or Eclipse?
This is misleading. These systems can be extended with plugins not because the OCP is a good design principle, but because they want external developers to be able to extend the functionalities of their applications. These are two different goals.
The codebases themselves are often modified when new functionalities need to be added to the application. In short, they don’t respect the OCP. Just look at Vim’s codebase to be convinced.
“This is wrong!” claims Dave, your colleague developer. “This is a principle! It shouldn’t be always applied! It depends on the context!”
I’m glad that Dave brought this point. But it’s not how Martin explains it; as often, his opinion is quite absolute. The OCP is the way to go, and that’s all. When Martin addresses the relevance of the SOLID principles in one of his blog’s article, he’s defending the OCP vigorously:
Of all the principles, the idea that anyone would question this one fills me full of dread for the future of our industry.
On the contrary: we should question what we do. We should thrive to understand the principles we use. We should understand if they’re good to use in a precise context. We should look at their potential benefits and trade-offs. We should listen to different opinions, to be sure ours are valid. We should look at every piece of information we find on the Internet (including this article) with a critical eye.
When should we use the OCP? Never. This is not a principle, it’s just a bad idea. You shouldn’t use abstractions like a magical remedy for everything which needs some change. If you do, you’ll take the risk to see your system collapses under the weight of its own complexity.
What should we do instead?
- Thinking carefully before creating abstractions and adding new layers of indirection.
- Managing the relationships of the different elements composing our system, to make them cohesive when we need to, decoupled otherwise, and to limit the propagation of changes.
Adding some sort of plugin system is a good idea if we want the users of our applications to be able to extend it. This has nothing to do with general architecture principles we should follow.
Solid or Fluid?
The SOLID principles are appealing because they’re explained in simple and absolute terms. When I read Martin’s prose, it seems that they solve everything. But our designs shouldn’t be solid like a stone we can’t really shape for our own needs, but fluid and flexible depending on the ever-changing business domain we want to codify. In this context, the OCP can make everything SOLID indeed, creating abstractions which are hard to modify. We don’t want that.
What did we see in this article?
- The OCP was first coined by Bertrand Meyer. For him, we should use inheritance of implementation to respect it.
- Even if Martin claims that he’s paraphrasing Meyer, he’s not. For Martin, we can use any form of abstraction to respect it, on any “entity” (classes, functions, and whatnot).
- Adding new functionalities to a codebase implies changing the existing code (refactoring), not only adding new code.
- Adding layers of abstraction can add layers of indirection too, making the system more complex to understand and reason about.
- The “plugin architecture” is just a way to give the ability to users to extend their favorite application. This is not related to the OCP.
Like many, I saw Martin as a guide and mentor when I began my journey as a developer. I had to question my beliefs and to go in different communities to understand that these principles are not as good as they seem. It proves to me that we should be as open-minded as possible, and we should question the ideas of those who claim that they shouldn’t be questioned.