There are many, many articles which you can refer to about the SOLID principles as a whole, but in a super concise and rough form, they are:
S – Single Responsibility Principle: Make each function fulfil one purpose only
O – Open-Closed Principle: Let’s come back to this one!
L – Liskov Substitution Principle: A given object shouldn’t ‘break’ any of its prototypes properties.
I – Interface Segregation Principle: Don’t make bloated interfaces.
D – Dependency Inversion Principle: Handing over control of a function to the caller/callback.
Firstly, let’s look at definitions: the Open-Closed Principle asserts that our modules should be “open to extension but closed to modification”. In other words, it should be possible to add onto a module (perhaps using additional functions, new parameters, new objects, etc.) but doing so won’t require a refactoring or rewriting of the module itself. This excellent article puts it in a way which is controversial in its application but illuminating in its philosophy:
"If I have to open the JS file your module and make a modification in order to extend it, you’ve failed the open closed principle."
For me, this was a lightbulb moment. Like make other beginners, I had been loading up huge .js files with very few functions, all of which were running dozens of lines long, doing plenty of different tasks and not being flexible to change. I had already re-evaluated this practice by reading about the Single Responsibility Principle, but something about the Open-Closed Principle made it all click into place.
Put simply: If my module and the functions within it need to be closed to modification but open to extension, then that has three key implications.
1. The purpose of the given code needs to be crystal clear
Without a clear goal or purpose, I run the risk of my module trying to ‘be everything to everyone’ and I am unlikely to be able to meet the demands of the Open-Closed principle. If I am either too broad or too specific, my code is likely to have information or attributes which are unnecessary, and are therefore more likely to require modification in future.
2. My code needs to be both flexible and airtight to accommodate for future extensions which could occur while still retaining a core of functionality
If I can foresee any sort of extension to my code in future, which I should, then I need to write code that is working to the peak of its ability but also remains simplistic enough to be flexible to the needs of others (or myself) in the future. This point is for me the key takeaway.
3. I am far more likely to need external inputs paired with truly useful return outputs
This is a realisation I only came to while crafting my code for my first post-SOLID-learning project: my todo list application. In order to adhere to the Open-Closed principle, I need to work on having functions draw down from other functions and modules rather than creating variables internally so as to allow for future extension should it be needed.
This final point intersects heavily with the Dependency Inversion principle, as my first point intersects heavily with the Single Responsibility Principle, but if anything, this just makes me more convinced of the value of the Open-Closed Principle as being the key principle to keep in mind while coding. If SOLID principles had no overlap and seemingly said wildly different things, I would be flailing around like a wacky waving inflatable arm-flailing tube man. Instead, I’m sitting here thrilled to be taking this journey with you and keen to see just how modular-yet-meaningful my code can get.