Software entities (classes, modules, functions, etc.) should be open
for extension, but closed for modification.
The purpose of this principle is to make it possible to change or
extend the behavior of an existing class without actually modifying the
source code to that class.
Making classes extensible in this way has a number of benefits:
Every time you modify a class, you risk breaking it, along with all
classes that depend on that class. Reducing churn in a class reduces
bugs in that class.
Changing the behavior or interface to a class means that you need to
update any classes that depend on the old behavior or interface.
Allowing per-use extensions to a class eliminates this domino
It may sound appealing to never need to change existing classes
again, but achieving this is difficult in practice. Once you’ve
identified an area that keeps changing, there are a few strategies you
can use to make it possible to extend without modifications. Let’s go
through an example with a few of those strategies.
In our example application, we have an Invitation class
that can deliver itself to an invited user:
# app/models/invitation.rbdef deliver body =InvitationMessage.new(self).bodyMailer.invitation_notification(self, body).deliverend
However, we need a way to allow users to unsubscribe from these
notifications. We have an Unsubscribe model that holds the
email addresses of users that don’t want to be notified.
The most direct way to add this check is to modify
# app/models/invitation.rbdef deliverunless unsubscribed? body =InvitationMessage.new(self).bodyMailer.invitation_notification(self, body).deliverendend
However, that would violate the open/closed
principle. Let’s see how we can introduce this change without
violating the principle.
This works adequately for creation, but using the ActiveRecord
pattern, we’ll end up with an instance of Invitation
instead, if we ever reload from the database. That means that
inheritance is easiest to use when the class we’re extending doesn’t
As you’ve followed along with these strategies, you’ve probably
noticed that although we’ve found creative ways to avoid modifying
Invitation, we’ve had to modify other classes. When you
change or add behavior, you need to change or add it somewhere. You can
design your code so that most new or changed behavior takes place by
writing a new class, but something, somewhere in the existing code will
need to reference that new class.
It’s difficult to determine what you should attempt to leave open
when writing a class. It’s hard to know where to leave extension hooks
without anticipating every feature you might ever want to write.
Rather than attempting to guess what will require extension in the
future, pay attention as you modify existing code. After each
modification, check to see if there’s a way you can refactor to make
similar extensions possible without modifying the underlying class.
Code tends to change in the same ways over and over, so by making
each change easy to apply as you need to make it, you’re making the next
Although monkey patching doesn’t literally modify the class’s source
code, it does modify the existing class. That means that you risk
breaking it, and, potentially, all classes that depend on it. Since
you’re changing the original behavior, you’ll also need to update any
client classes that depend on the old behavior.
In addition to all the drawbacks of directly modifying the original
class, monkey patches also introduce confusion, as developers will need
to look in multiple locations to understand the full definition of a
In short, monkey patching has most of the drawbacks of modifying the
original class without any of the benefits of following the open/closed
Although following the open/closed
principle will make code easier to change, it may make it more
difficult to understand. This is because the gained flexibility requires
introducing indirection and abstraction. Although all of the three
strategies outlined in this chapter are more flexible than the original
change, directly modifying the class is the easiest to understand.
This principle is most useful when applied to classes with high reuse
and potentially high churn. Applying it everywhere will result in extra
work and more obscure code.