LESSON 2.2
Open/closed principle
Add new behavior by adding new code, not by editing existing code. Variants expressed as branches always grow.
6 min read
A payment gateway starts with two types: credit and debit. Six months later the product adds PayPal, then crypto, then buy-now-pay-later. Each addition means editing the same processPayment switch, risking regressions in every existing branch.
The problem
class PaymentProcessor {
charge(order, type) {
if (type === "credit") return `charged $${order.amount} to card`;
if (type === "debit") return `debited $${order.amount}`;
if (type === "paypal") return `sent $${order.amount} via PayPal`;
// Adding "crypto" means editing this method — and re-testing credit and debit.
throw new Error("Unknown payment type");
}
}The method grows by one branch per new type. Every new type is a regression risk for the existing types, and the class must be touched — and re-reviewed — for each addition.
The rule
Open/closed principle: software entities should be open for extension but closed for modification. Once a class is tested and deployed, adding a new variant should require adding new code, not changing existing code.
The classical mechanism is polymorphism: define a stable interface, and let each variant implement it. The processor stays closed; the new class opens the extension point.
In code
When OCP pays and when it does not
OCP costs one interface and one class per variant, and a factory or injection point to choose between them. That cost earns its keep when:
- The set of variants grows over time.
- Each variant is tested independently.
- Variants live in different modules or are owned by different teams.
It does not pay when there are two variants that are unlikely to grow, or when the branching logic is a single null-check. Applying OCP to a two-branch condition produces three files, two tests and a pattern name where one function would do.
Where you have already seen it
Java's Comparator lets callers sort any collection without changing List.sort. Express middleware follows OCP: the framework does not change when you add a middleware. The strategy stored in a React context makes a UI open to new themes without touching rendering components.
Try it
Coupon Rule Engine LLD
Define a Coupon interface and implement percentage and fixed-amount variants before adding a buy-one-get-one type.