Clean Code Principles
A Beginner's Lecture Guide
DRY · KISS · YAGNI · SOLID
This guide teaches eight essential software engineering principles
using everyday analogies and simple Python examples.
No prior experience with advanced OOP required.
Page 1
Clean Code Principles – Beginner's Guide
Table of Contents
1. DRY – Don't Repeat Yourself
2. KISS – Keep It Simple, Stupid
3. YAGNI – You Aren't Gonna Need It
4. SOLID – S: Single Responsibility
5. SOLID – O: Open/Closed Principle
6. SOLID – L: Liskov Substitution
7. SOLID – I: Interface Segregation
8. SOLID – D: Dependency Inversion
9. Quick Reference Summary Table
Page 2
Clean Code Principles – Beginner's Guide
1. DRY – Don't Repeat Yourself
"Every piece of knowledge must have a single, unambiguous representation."
Analo You have a phone number written on 10 sticky notes. When it changes, you must change all
gy: 10. Better: save it once in your phone's contacts.
■ Bad – repeated code
# Calculating area – repeated formula
area1 = 3.14 * 5 * 5 # radius 5
area2 = 3.14 * 7 * 7 # radius 7
area3 = 3.14 * 10 * 10 # radius 10
print(area1, area2, area3)
■ DRY – one formula, reused
def circle_area(radius):
return 3.14 * radius * radius
print(circle_area(5))
print(circle_area(7))
print(circle_area(10))
Now if 3.14 changes to 3.14159, you fix only ONE place. This reduces bugs and saves time
whenever requirements change.
2. KISS – Keep It Simple, Stupid
"Simple solutions are usually the best solutions."
Analo To open a door, use a key – not a rocket launcher. Simple solution wins.
gy:
■ Overly clever – hard to read
def is_even(n):
return not (n & 1) # beginners have no idea what & does
■ KISS – clear and simple
def is_even(n):
return n % 2 == 0
Even a child can read n % 2 == 0 and understand: remainder when dividing by 2 equals zero. Clever
code is hard to debug and maintain. Simple code is a gift to your future self.
Page 3
Clean Code Principles – Beginner's Guide
3. YAGNI – You Aren't Gonna Need It
"Don't build features until they are actually needed."
Analo You're building a doghouse. Don't add a second floor 'just in case you get a giraffe'. You won't
gy: need it.
■ Adding useless 'maybe later' code
def make_coffee(type="espresso"):
if type == "latte":
froth_milk() # not needed yet
if type == "cappuccino":
add_chocolate() # not needed yet
print("Here's your", type)
■ YAGNI – only what you need now
def make_coffee():
print("Here's your espresso")
When you actually need latte or cappuccino, THEN add the extra code. Unnecessary code adds
complexity, bugs, and maintenance cost for features that may never be used.
Page 4
Clean Code Principles – Beginner's Guide
SOLID Principles
4. S – Single Responsibility Principle
"A function (or class) should do one thing well."
Analo A Swiss Army knife has many tools – but you don't use the can opener to brush your teeth.
gy: Each tool has one job.
■ Bad – one function doing two jobs
def save_and_print_user(name):
print(f"Hello, {name}") # job 1: print
# saving to file here # job 2: save
print("User saved")
■ Good – split into two functions
def greet_user(name):
print(f"Hello, {name}")
def save_user(name):
print(f"Saving {name} to file")
Now you can change the greeting without touching the saving logic. Each function is easier to test,
understand, and reuse independently.
5. O – Open/Closed Principle
"Open for extension, closed for modification."
Analo A power outlet: you can plug in a lamp, a fan, or a phone charger – you don't have to rewire
gy: the wall socket every time.
■ Bad – changing old code for each new shape
def area(shape, size):
if shape == "square":
return size * size
elif shape == "circle":
return 3.14 * size * size
# Want triangle? Must modify this function again.
■ Good – extend without modifying
Page 5
Clean Code Principles – Beginner's Guide
def square_area(side):
return side * side
def circle_area(radius):
return 3.14 * radius * radius
# To add triangle: just write a new function
# No need to touch the existing functions
Add new shapes by writing new functions, not by changing old ones. This prevents introducing bugs
into already working code.
Page 6
Clean Code Principles – Beginner's Guide
6. L – Liskov Substitution Principle
"A child class must truly act like its parent."
Analo If you ask for a 'bird that can fly', a sparrow works – but a penguin does not. Don't call a
gy: penguin a 'flying bird'.
■ Bad – child breaks parent's promise
class Bird:
def fly(self):
return "Flying"
class Penguin(Bird):
def fly(self):
return "Penguins can't fly!" # Breaks the promise
make_it_fly(Penguin()) # confusing!
■ Good – only inherit what you truly are
class Bird:
pass # no fly method
class FlyingBird(Bird):
def fly(self):
return "Flying"
class Penguin(Bird):
def swim(self):
return "Swimming"
# make_it_fly only accepts FlyingBird
If a child class can't honour a parent's behaviour, redesign the hierarchy. Violating LSP causes
unexpected bugs when you swap objects of related types.
7. I – Interface Segregation Principle
"Don't force a class to have methods it doesn't need."
Analo A restaurant menu: don't make a vegetarian order from the meat section. Give them their own
gy: small menu.
■ Bad – forced to implement unused methods
Page 7
Clean Code Principles – Beginner's Guide
class Worker:
def work(self): pass
def eat(self): pass
class Robot(Worker):
def work(self): print("Working")
def eat(self):
raise Exception("Robots don't eat")
■ Good – separate small interfaces
class Workable:
def work(self): pass
class Eatable:
def eat(self): pass
class Robot(Workable):
def work(self): print("Working")
# No eat method – perfect!
class Human(Workable, Eatable):
def work(self): print("Working")
def eat(self): print("Eating")
Robot only implements what it actually needs. Fat interfaces create tight coupling and force classes
to carry dead weight code that can cause runtime errors.
Page 8
Clean Code Principles – Beginner's Guide
8. D – Dependency Inversion Principle
"Depend on abstractions, not concrete implementations."
Analo A laptop shouldn't be built to work only with one brand of charger. Both should follow a
gy: standard USB-C agreement.
■ Bad – hard-coded dependency
class Notification:
def __init__(self):
[Link] = EmailSender() # stuck with email!
def alert(self, msg):
[Link](msg)
■ Good – depend on an agreement
class Notification:
def __init__(self, sender): # accepts any sender with .send()
[Link] = sender
def alert(self, msg):
[Link](msg)
notify = Notification(EmailSender())
[Link]("Hello")
notify2 = Notification(SMSSender())
[Link]("Hello")
Notification doesn't care what sender it uses – as long as it has a send() method. This makes it easy
to swap implementations, add new senders, and write unit tests.
Page 9
Clean Code Principles – Beginner's Guide
Quick Reference Summary
Principle One-Sentence Rule Simple Analogy
Save a phone number once, not on 10 sticky
DRY Don't write the same code twice.
notes.
KISS Keep code simple, not clever. Use a key to open a door, not a rocket launcher.
YAGNI Don't add code until you need it. Build a doghouse, not a giraffe tower.
SRP One function = one job. A spoon is for soup, a fork is for salad.
OCP Add new features without changing old code. Add a lamp to an outlet – don't rewire the wall.
LSP A child class must act like its parent. Don't call a penguin a 'flying bird'.
ISP Don't force unused methods on classes. Don't give a vegetarian a meat menu.
DIP Depend on agreements, not specifics. Use USB-C, not a custom charger.
These principles apply equally well to functions, classes, and entire systems. Master them and your
code will be cleaner, more maintainable, and a joy to work with.
Page 10