OOP Principles That Will Take You Far

tacoda1 pts0 comments

OOP Principles That Will Take You Far | by Ian Johnson | Jun, 2026 | MediumSitemapOpen in appSign up<br>Sign in

Medium Logo

Get app<br>Write

Search

Sign up<br>Sign in

OOP Principles That Will Take You Far

Ian Johnson

8 min read·<br>Just now

Listen

Share

The code was supposed to be a small change. Add a discount code field, run the existing total through it, update the invoice. Two hours, tops.<br>Three days later, I had touched seventeen files. The Order class knew about the database, the email service, the audit log, and the printer. Every change rippled. Every test required a small army of mocks. The discount code worked, but I left the code worse than I found it.<br>That experience, repeated enough times, is how you learn to care about design. Not the abstract kind. The kind you reach for at three in the afternoon when a ticket is bigger than it looked.<br>The principles below have been around for decades. They are not novel. They survive because they keep paying out. Most of them push you toward the same target from different angles: code that does one thing, says what it means, and changes without panic.<br>SOLID, in one page<br>SOLID is five rules, often taught poorly. Here they are with the marketing stripped out.<br>Single Responsibility. A class has one reason to change. If you find yourself saying “this class handles X and Y,” that’s two classes. The Order from my opening paragraph was a poster child: pricing logic and persistence and notification, all in one bag.<br>Open/Closed. A class is open for extension and closed for modification. In practice this means: when a new case shows up, you add a new class rather than threading another if through an old one. Polymorphism is the mechanism.<br>Liskov Substitution. A subtype should work anywhere the parent works without surprising the caller. If Square extends Rectangle but breaks every test that sets width and height independently, the inheritance was a lie. Most violations come from forcing an is-a relationship that the domain didn’t actually have.<br>Interface Segregation. Many small interfaces beat one fat one. A Reader and a Writer are more useful than a ReaderWriterCloserSeekerScanner. Callers should depend only on what they use.<br>Dependency Inversion. High-level code should not depend on low-level code; both should depend on abstractions. The pricing engine doesn’t import the MySQL driver. It accepts a PriceRepository, and the wiring is done at the edges.<br>The five letters are easier to remember as one sentence: small classes with one reason to change, depending on narrow interfaces, composed from the outside.<br>Program to an interface, not an implementation<br>This one shows up in the Gang of Four book and gets quoted into the ground, but the practical version is simple.<br>When you write def send(notifier), you can pass anything that responds to notify. An email sender, a Slack client, a queue writer, a fake for tests. When you write def send(mailer: EmailMailer), you have nailed yourself to one implementation. The first version costs you nothing extra and buys you every future swap for free.<br>This is not a license to invent interfaces for everything. One implementation, no test double, no anticipated variation? Don’t make the interface yet. The principle is about the shape of the dependency, not the number of files.<br>Tell, don’t ask<br>Old principle, still underrated. If you find yourself pulling data out of an object, making a decision, and pushing the result back in, you have written the object’s logic on the outside. Move it back in.<br># Asking<br>if account.balance >= amount<br>account.balance -= amount<br>end

# Telling<br>account.withdraw(amount)The second version hides the rule. If overdrafts get allowed next quarter, one class changes. The first version is the same logic, scattered across every caller.<br>A related rule is the Law of Demeter: a method should talk to its parameters, its fields, objects it creates, and not much else. Long a.b.c.d.e chains are a smell that something in the middle should be doing the work for you.<br>DRY, but not the way you were taught<br>DRY is “don’t repeat yourself.” The popular reading is “if you see two similar pieces of code, deduplicate them.” That reading has caused more damage than copy-paste ever did.<br>The real meaning, from Hunt and Thomas, is closer to this: every piece of knowledge in the system should have one authoritative representation. The duplication that matters is conceptual, not textual.<br>Two functions that look alike but encode different ideas should stay separate. Two functions that look different but encode the same idea should be unified. Refactor on the third occurrence, not the second. Until then, the shape of the duplication is still telling you something.<br>YAGNI<br>You aren’t gonna need it. The flexibility you build today for the case you imagine tomorrow is almost always wrong. The interface you over-design becomes a constraint when the real requirement arrives. The plugin system gets shipped, never plugged into, and now you maintain it...

code class from principles small change

Related Articles