design·lab

O · SOLID

Open / Closed Principle

Software should be open for extension but closed for modification. Add new behavior by adding new code, not by editing code that already works.

✗ The problem

The growing if / else

Every new shipping method forces an edit to calculateCost. Click a new requirement — watch it reopen a function that already worked.

function calculateCost(order) {
if (order.method === 'standard') return order.weight * 2;
if (order.method === 'express')  return order.weight * 5;


}
calculateCost()
knows every method
Each edit re-touches a tested function. More ifs = more risk, harder tests, merge conflicts. The function is never "done".
✓ The refactor

Depend on an abstraction

Define a common shape. Each method becomes its own small, self-contained strategy.

interface ShippingMethod {
  cost(order): number
}

class Standard implements ShippingMethod {
  cost(o) { return o.weight * 2 }
}
class Express implements ShippingMethod {
  cost(o) { return o.weight * 5 }
}
ShippingMethod
interface · cost()
Standard
Express
✓ Extend by adding

New method? New class. Nothing else changes.

The calculator just calls method.cost(order). Add a class, register it — done.

// unchanged, forever:
function calculateCost(order, method) {
  return method.cost(order);
}

// new requirement = a brand new file,
// zero edits to the code above
class Drone implements ShippingMethod {
  cost(o) { return o.weight * 9 + 20 }
}
ShippingMethod
Standard
Express
Drone
added later ✨
✓ Takeaway

Closed to edits, open to growth

  • Smell: a switch/if-else chain you re-edit for every new case.
  • Fix: program to an interface; put each variant behind it.
  • Payoff: tested code stays untouched; new features can't regress old ones.
  • Careful: add the abstraction when the 2nd or 3rd case appears — not preemptively (YAGNI).
Related: this is the pattern mindset → — small interchangeable pieces behind a stable contract.