Do You Use the Most Crucial Principle of Object-Oriented Design?
The open-closed principle in action

The most crucial principle of object-oriented design is the The open-closed principle (OCP).
It took me a while to understand the the OCP. I found the contradiction “modules should be both open and closed” vague.
It wasn’t until after I read “Refactoring: Improving the Design of Existing Code” from Martin Fowler that it all started to make sense.
In this article, I’ll describe my point of view on the OCP. I’ll use two real-world examples — one from a previous project and another from an open-source project on GitHub.
I’ll end by sharing some heuristics to detect source code you can improve using the OCP.
The Definition of the OCP
A bit of history:
The OCP is one of the five principles that Bertrand Meyer described in 1988 in his book “Object-Oriented Software Construction.” These five principles of software construction describe how to create a modular system using object-oriented concepts.
In the book, Bertrand described the OCP as:
“Modules should be both open and closed.
• A module is said to be open if it is still available for extension.
• A module is said to be closed if it is available for use by other modules.”
In 2000, Robert C. Martin described the principle in “Design Principles and Design Patterns” as:
“We want to be able to change what the modules do, without changing the source code of the modules.”
Later, Michael Feathers came up with the acronym SOLID that stood for five important principles from “Design Principles and Design Patterns” from Robert C. Martin. This increased the popularity of the principles.
The O in SOLID stands for the open-closed principle
What’s the reason behind the OCP?
There’s a single question behind the OCP and the other object-oriented design principles:
How can we design a system in such a way that it becomes easy to add new features to an application codebase?
What Does the OCP Mean?
The OCP states we should be able to change what a module does, without changing the source code.
Doesn’t this sound strange? How can we change the behavior of a module without changing the source code? This was precisely my struggle when I first learned this principle.
Open for extension but closed for modification
The OCP starts to make a bit more sense if we look at another explanation: “A module should be open for extension but closed for change.”
So we can add something new — but can’t change the existing source code.
Now, we’re starting to get somewhere. So I can add a class but can’t change an existing one.
So how can we design an application that alters its behavior when adding a new class? There’s a straightforward answer:
By using inheritance and polymorphism.
The First Example: IoT Sensor Measurement

Let’s look at an example. We have an IoT solution that reads measurements from different types of sensors. These sensors measure temperature, humidity, CO2, and pressure.
Before the application can use the measurement, it must convert the value.
The conversion is different for each type of sensor. Some directly return the correct value, some use a linear conversion, and still others use a more complicated transformation.
Conversion not following the OCP
The module below shows a function for converting the value.
The function accepts the type of sensor and gross value. Depending on the sensorType
, the function executes a specific conversion.
Currently, the function supports three types of sensors: PT500
, ERH
, and CLIMAPREC
. If we have to support a new kind of sensor, we have to change it by adding a new case to the switch.
It’s possible that when we make this change, we’ll introduce bugs into the existing conversions. The introduction of bugs while changing existing code is what the OCP tries to prevent.
This module, thusly, doesn’t follow the OCP. It’s not closed for modification.
How can we change this conversion function so it applies to the OCP?
Conversion using the OCP
Instead of having a single conversion function, we introduce multiple sensor classes. Each sensor class is responsible for the conversion of a specific sensor type.
The function that performs the actual conversion uses a collection of sensor types. The sensor builder module creates this collection.
The MeasurementConverter
uses the given sensorType
as a key to retrieve the correct sensor
class.
We call the convert
function on this retrieved sensor
class.
If we have to support a new sensor type, we add a new class for that specific sensor and implement the convert function.
So we’re adding a new class instead of changing the existing code. We don’t have the risk of introducing bugs into the existing code.
This is the OCP in action.
The Second Example: Fastify Register Routes

Did you notice in the previous example I still had to change the source code that fills the sensor dictionary? If we’re strict, it’s not entirely open-closed. If I also wanted to remove this change, I’d have to load the sensors from the file system.
Fastify Register Routes is a Fastify plugin created by Israel Ériston that loads routes from a specified path and registers them with Fastify.
It’s interesting to see how Israel implemented this functionality. Below we can see part of the source code.
On line 8, he retrieves all of the available route files from a specific directory. Then on line 9, he calls require
on each file using .map
. This results in an array with route functions.
He then calls registerRoutes
for each loaded route. This registers each route with Fastify.const routes = loadRoutesByPath(dirname, options);
routes.forEach(route => registerRoutes(server, route));
The result is you can add routes to your API by adding a JavaScript file that defines a route. So there’s no need to change the existing source code.
It’s another beautiful example of the OCP.
How to Recognize Source Code That Can Be Improved
So how can you recognize places in your solution that can be improved using the OCP?
I found a couple of tips in the book “Refactoring: Improving the Design of Existing Code” from Martin Fowler. This is an excellent book, by the way.
Replace a conditional with a polymorphism
One of the refactorings done in the book is involves replacing a conditional with a polymorphism. You can apply this refactoring if:
“You have a conditional that chooses different behavior depending on the type of an object.”
This is precisely the situation of the previous sensor example. Depending on the type of sensor, I use a different conversion function. To make it even more concrete: If you have a switch in your source code that uses a type code, it’s a possible place to improve.
If instead of a switch, your source code contains an if-else-if construction based on a type code, this may also be a place to improve.
So whenever you see a switch in your source code, I want you to think about the OCP. Can you improve the design through inheritance and polymorphism?
Conclusion
The OCP is one of the most important principles of object-oriented design. If you use it correctly, it’ll make your source code more modular and maintainable.
Two real-world examples showed how to use the OCP. The first one uses the OCP to convert gross measurements to net values. In the second one, the Fastify plugin uses the OCP to add routes.
Finally, I want you to ask yourself a question whenever you see a switch or an if-else-if construction in your source code. This question is: “Can I apply the OCP to improve the design of the source code?”
Thank you for reading.