The open/closed principle is a tricky one, and I think it's often misunderstood. In the strict sense, obviously it's impossible to add behaviour to an application without modifying its code, but what we really mean by open/closed is that it should be possible to extend the functionality of a given class without changing that class. Let me try to come up with a simple contrived example. Let's say you have an Order class, which has a price method that sums the prices of all the items in the order:
class Order
def price
items.inject(0) { |total, item| total += item.price }
end
end
Now, say you have a new requirement that some orders will be taxable (for customers in the same state, e.g.) and others won't be. Regardless of whether you want to adhere to O/C, you're going to have to change the Order class. That's just inevitable. But how do you do it? The obvious approach is a conditional:
class Order
def price
base_price = items.inject(0) { |total, item| total += item.price }
if taxable?
base_price * 1.1
else
base_price
end
end
end
However, the thing to realise here is that the next time someone requests a change to how order prices are calculated, you will still have to make another change to this class. And as those changes stack on top of each other things get out of hand. So what open/closed says is that you should refactor the existing functionality in such a way that it becomes possible to make the change without altering Order.
There are a million ways of doing this, but for the sake of argument let's say you want to employ a sort of strategy pattern: your Order depends on one of many PriceCalculator subclasses to compute the price, and the calculator is injected when you instantiate an Order. Now, when you inevitably get the request to add a shipping fee to some orders (but not others), you can swap out the PriceCalculator with a PriceCalculatorWithShipping (or whatever), and not have to touch Order.
So to sum up, I'd say open/closed isn't discouraging code changes, it's just trying to make sure those changes happen in as few places as possible, and that you have fewer reasons to make extensive modifications to existing classes. I'd argue that in the medium term this makes applications much easier to change, and so is entirely compatible with the agile philosophy.
For a really great longer-form exposition of these ideas I'd recommend this talk by Sandi Metz: https://www.youtube.com/watch?v=8bZh5LMaSmE.