Design Principles: DRY, KISS, Coupling and Cohesion โ Revision Notes
Clean-code principles โ DRY, KISS, coupling, and cohesion โ show design sense beyond making code work. "What is low coupling / high cohesion?" and "what does DRY mean?" are frequent interview questions that reveal how you think about maintainability.
The acronyms
- DRY (Don't Repeat Yourself) โ every piece of knowledge should have a single source of truth; eliminate duplication so a change is made in one place.
- KISS (Keep It Simple, Stupid) โ favour the simplest solution that works; avoid needless complexity.
- YAGNI (You Aren't Gonna Need It) โ don't build features until they're actually required.
Coupling and cohesion (the big pair)
- Coupling โ the degree of interdependence between modules. Low/loose coupling is good (modules change independently).
- Cohesion โ how strongly the elements within a module belong together. High cohesion is good (a module does one well-defined job).
The goal: low coupling + high cohesion โ the most cited maintainability principle.
| Property | Good value | Meaning |
|---|---|---|
| Coupling | low | modules independent |
| Cohesion | high | module is focused |
Exam Tricks & Tips
- ๐ฏ The golden rule: low coupling, high cohesion โ modules independent of each other, focused internally.
- ๐ฏ DRY = single source of truth (no duplicated logic); a change should be made in exactly one place.
- ๐ฏ KISS = simplest thing that works; YAGNI = don't build it until needed โ both fight over-engineering.
- ๐ฏ High coupling is bad (a change ripples across modules); low cohesion is bad (a module does unrelated things).
- ๐ฏ Separation of concerns โ split a system so each part addresses a distinct responsibility (drives high cohesion).
- ๐ฏ These principles reduce maintenance cost and bug propagation โ the "why" to state.
- โ Common mistake: reversing the goal โ you want coupling low and cohesion high, not the other way around.
Expected pattern
"What is DRY/KISS/YAGNI?", "explain coupling and cohesion", "what's the ideal combination?", "why is high cohesion good?", and "what is separation of concerns?".
Quick recap
DRY = single source of truth (no duplication); KISS = simplest solution; YAGNI = don't build until needed. Coupling = inter-module dependence (want it low); cohesion = intra-module focus (want it high). The maintainability goal is low coupling + high cohesion, driven by separation of concerns.
Design Principles: DRY, KISS, Coupling and Cohesion โ Flashcards
Cover the answer, recall, then check. 11 cards on design principles.
Q1. What does DRY stand for and mean?
A1. Don't Repeat Yourself โ avoid duplication so each piece of knowledge has a single source of truth.
Q2. What does KISS mean?
A2. Keep It Simple, Stupid โ prefer the simplest solution that works and avoid unnecessary complexity.
Q3. What does YAGNI mean?
A3. You Aren't Gonna Need It โ don't implement functionality until it is actually required.
Q4. What is coupling?
A4. The degree of interdependence between software modules; loose/low coupling is desirable.
Q5. What is cohesion?
A5. How strongly the elements within a module belong together; high cohesion (a focused module) is desirable.
Q6. What is the ideal combination of coupling and cohesion?
A6. Low coupling and high cohesion โ independent modules that are each internally focused.
Q7. Why is high coupling bad?
A7. A change in one module ripples into others, making the system fragile and hard to maintain.
Q8. Why is low cohesion bad?
A8. A module doing many unrelated things is harder to understand, test, and reuse.
Q9. What is separation of concerns?
A9. Dividing a system so each part handles a distinct responsibility, which promotes high cohesion and low coupling.
Q10. How does DRY reduce bugs?
A10. With one source of truth, a fix is applied in a single place instead of many, avoiding inconsistent duplicates.
Q11. How do KISS and YAGNI relate?
A11. Both combat over-engineering โ KISS keeps designs simple; YAGNI avoids building speculative, unneeded features.
Design Principles: DRY, KISS, Coupling and Cohesion
Beyond making code work, good engineers make it maintainable. A handful of timeless principles โ DRY, KISS, YAGNI, and the pairing of low coupling / high cohesion โ capture what separates clean code from a mess, and they recur constantly in interviews.
Core idea: these principles guide code toward being simple, non-repetitive, and well-structured. DRY eliminates duplication, KISS favours simplicity, YAGNI avoids speculative features, and the twin goals of low coupling (modules minimally dependent) and high cohesion (each module does one focused thing) drive good modular design.
How it works
Beginner โ the acronym principles
- DRY (Don't Repeat Yourself): every piece of knowledge should have a single, authoritative representation. Duplicated logic means fixing a bug in one place and missing the copies.
- KISS (Keep It Simple, Stupid): prefer the simplest solution that works; complexity is a cost, not a virtue.
- YAGNI (You Aren't Gonna Need It): don't build features "for the future" you don't need now โ they add complexity and often go unused.
Intermediate โ coupling and cohesion
- Coupling = how much modules depend on each other. Low (loose) coupling is the goal: a change in one module shouldn't ripple into others. Tight coupling makes systems fragile.
- Cohesion = how focused a single module is. High cohesion is the goal: a module's parts all serve one well-defined purpose. Low cohesion (a "god class" doing unrelated things) is a smell.
// High coupling (bad): OrderService reaches into MySQL internals directly
// Low coupling (good): OrderService depends on a Repository interface
The mantra: "low coupling, high cohesion."
Advanced โ how the principles interact
These principles reinforce each other but can also tension:
- Extracting duplicated code (DRY) usually raises cohesion and can lower coupling โ but over-DRYing unrelated code that merely looks similar creates a false abstraction and increases coupling (a wrong "shared" dependency). Duplication is cheaper than the wrong abstraction.
- KISS and YAGNI guard against over-engineering โ the elaborate, "flexible" design that DRY zealotry or speculative generality can produce.
- Low coupling is enabled by abstractions/interfaces (Dependency Inversion) and is what makes code testable (you can mock a loosely-coupled dependency).
Worked example โ refactoring toward the principles
A UserManager class validates emails, saves to the database, sends welcome emails, and formats reports โ plus the email-validation regex is copy-pasted in three methods.
- DRY violation: the triplicated regex โ extract one
isValidEmail()method (single source of truth; fix it once). - Low cohesion: the class does four unrelated jobs โ split into
UserValidator,UserRepository,EmailService,ReportFormatter(each highly cohesive โ one responsibility, echoing SRP). - High coupling: if it built its own DB connection โ inject a repository interface instead, so swapping databases or mocking in tests touches nothing.
The result: smaller, focused, loosely-coupled classes that are far easier to test and change โ a concrete demonstration of the principles working together.
Interview & exam relevance
Common asks: "what is DRY / KISS / YAGNI", "difference between coupling and cohesion and which you want high/low", "give an example of tight coupling and how to reduce it" (introduce an interface/dependency injection), and "what is a code smell". These often connect back to SOLID (SRP โ high cohesion; DIP enables low coupling).
Tricks & gotchas
- Low coupling, high cohesion โ memorise which you want high (cohesion) and which low (coupling).
- DRY has limits: don't merge code that's only coincidentally similar โ "duplication is cheaper than the wrong abstraction."
- YAGNI counters gold-plating; KISS counters cleverness for its own sake.
- Mnemonic: "Cohesion together, Coupling apart" โ parts within a module belong together; different modules stay independent.
Getting coupling and cohesion backwards โ aiming for "high coupling" or "low cohesion". You want low coupling (modules independent, so changes don't ripple) and high cohesion (each module focused on one job). Reversing them describes a fragile, tangled design, yet the symmetry of the terms makes this a frequent slip.
- โ- DRY: one authoritative representation of each piece of logic โ eliminate duplication.
- โ- KISS: prefer the simplest solution; YAGNI: don't build unneeded speculative features.
- โ- Coupling = inter-module dependency; aim for LOW (loose) coupling.
- โ- Cohesion = intra-module focus; aim for HIGH cohesion (one responsibility).
- โ- Over-applying DRY to coincidentally-similar code creates wrong abstractions and more coupling.
- โMaintainable code follows DRY (no duplication), KISS (simplicity), and YAGNI (no speculative features), and is structured for low coupling (independent modules) and high cohesion (focused modules). These principles reinforce SOLID and each other โ but avoid over-DRYing unrelated code.
Design Principles: DRY, KISS, Coupling and Cohesion โ Worked Example
Worked Example
Problem: Critique this code against DRY, KISS, coupling, and cohesion, then refactor. Explain how each principle applies.
class Report:
def total_with_tax(self, items):
t = 0
for i in items: t += i.price
return t + t * 0.18 # tax logic duplicated below
def invoice_with_tax(self, items):
t = 0
for i in items: t += i.price # same summation duplicated
return t + t * 0.18 # same tax rate hard-coded again
def send_email(self, addr, body): # unrelated responsibility here
...
Solution:
DRY (Don't Repeat Yourself): the summation loop and the tax formula (+ t * 0.18) are duplicated in two methods, and the rate 0.18 is a magic number repeated. If the tax rate changes, you must edit multiple places and may miss one. Extract the repeated logic into a single source of truth.
Cohesion (how focused a module is): Report mixes financial calculation with send_email โ an emailing responsibility unrelated to reporting. That is low cohesion; a class should group logically related behaviour. Move emailing to its own class/module so Report is cohesively about reports.
Coupling (how dependent modules are on each other): if the tax rate is hard-coded inside Report, callers and related classes are tightly coupled to that constant. Centralising it (a constant or an injected TaxPolicy) reduces coupling โ other code depends on an abstraction, not a scattered literal.
KISS (Keep It Simple): the design should be as simple as the requirement allows โ small, single-purpose helpers, no needless duplication or unrelated features bundled in.
Refactor:
TAX_RATE = 0.18 # single source of truth (DRY)
def subtotal(items): # one summation, reused (DRY)
return sum(i.price for i in items)
def with_tax(amount, rate=TAX_RATE):
return amount + amount * rate
class Report: # cohesive: only reporting math
def total(self, items): return with_tax(subtotal(items))
def invoice(self, items): return with_tax(subtotal(items))
class Mailer: # emailing lives here (high cohesion)
def send(self, addr, body): ...
Now the tax rate and summation each exist once (DRY), Report is cohesive (only report math), emailing is decoupled into Mailer, and each helper is small and obvious (KISS). A tax-rate change is a one-line edit.
Answer: The original repeats the summation and tax formula (violates DRY, with a magic-number rate), and bundles emailing into Report (low cohesion / poor separation). Refactor by extracting subtotal and with_tax as single sources of truth, centralising TAX_RATE (loosening coupling), and moving emailing to a Mailer class so each unit is cohesive and simple (KISS).
- โ- DRY: every piece of logic/knowledge should have one authoritative definition; eliminate duplication and magic numbers.
- โ- High cohesion = a module does one related job; low coupling = modules depend on abstractions, not scattered internals.
- โ- KISS: prefer the simplest design that meets the requirement โ small, single-purpose, obvious units.