Lesson 4 of 6 · 12 min · 1 hands-on step
SOLID Principles in Plain English
Five design rules that keep code easy to change, with a hands-on refactor.
SOLID, one sentence each
| Letter | Principle | Plain English |
|---|---|---|
| S | Single responsibility | Each function or class does one job |
| O | Open/closed | Add new behavior by adding code, not editing working code |
| L | Liskov substitution | A child class should work anywhere its parent does |
| I | Interface segregation | Don't force things to depend on methods they don't use |
| D | Dependency inversion | Depend on what something does, not which specific thing it is |
S: one job each
// ❌ Calculates, formats AND emails. Three reasons to change.
function processOrder(order) { /* ... */ }
// ✅ Each piece can change, and be tested, on its own
const subtotal = (items) => items.reduce((s, i) => s + i.price * i.qty, 0);
const formatReceipt = (total) => `Total: $${total.toFixed(2)}`;
console.log(formatReceipt(subtotal([{ price: 9.99, qty: 2 }, { price: 5, qty: 1 }])));O: extend without editing
A long if/else chain on a "type" is a warning sign. Every new type means editing (and possibly breaking) it. A lookup table of strategies lets you add without touching what works:
const discounts = {
none: (total) => total,
student: (total) => total * 0.8,
blackFriday: (total) => total * 0.5,
};
const checkout = (total, type) => discounts[type](total);
console.log(checkout(100, "student"));D: depend on "what", not "which"
// The report doesn't care HOW we notify. Email, SMS, Slack… pass any notifier in.
function sendReport(report, notify) {
notify(`Daily report: ${report}`);
}
sendReport("42 new users", (msg) => console.log("[email]", msg));
sendReport("42 new users", (msg) => console.log("[sms]", msg));Passing dependencies in (instead of hard-coding them) is also what makes code testable: in tests you pass a fake notifier.
Don't memorize SOLID like a poem. Notice the pain each rule prevents: code that's scary to change.
🧪 Practice
Every new shipping method means editing this if chain, which risks breaking the others. Refactor shippingCost to look up a strategy object instead:
- keep the exact same results for
standard(5),express(15) andfree(0) - add a new method
overnightcosting25without writing a newif - unknown methods should throw an
Error
function shippingCost(method) {
if (method === "standard") return 5;
else if (method === "express") return 15;
else if (method === "free") return 0;
else throw new Error("Unknown shipping method");
}
Output appears here. Press Run or Ctrl/⌘ + Enter.
✅ Check your understanding
What does the Single Responsibility Principle say?
Create a free account to track your progress.