Open-Close Principle at micro level

Some concepts are hard to grasp because of lots of details or because the level of abstraction is too high. Recently, I was asked about the Open-Close principle and how I would explain it. It seems to me, that the simplest explanation is when you show it on the micro level example.

Let’s take a very simple example for the simplicity of narration.

function isSilent() {
    if (process.env.NODE_ENV === 'production') {
        return true;
    }

    return false;
}

Everything is simple and clean so far. But what if the requirements have changed and there are more than one environment?

function isSilent() {
    if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging') {
        return true;
    }

    return false;
}

When you find yourself in a situation where you need to change the production code (especially, some conditions), this is a clear sign that something is going wrong. How can we improve this situation? One of the ways is to separate data from the logic, make the logic more flexible and move the data somewhere else.

const PRODUCTION_ENVS = ['production', 'staging'];

function isSilent() {
    if (PRODUCTION_ENVS.includes(process.env.NODE_ENV)) {
        return true;
    }

    return false;
}

Is it open for extension? Yes, it is.

"Open for extension" means that the behavior of the module can be extended. As the requirements of the application change, we are able to extend the module with new behaviors that satisfy those changes.

Is it close for modification? Yes, it is.

"Closed for modification" means that the source code of the module cannot be changed. The behavior of a module does not result in changes to the source code of the module.

So, this is how Open-Close principle generally works, but it happens at a higher level of abstraction.