When we write code, our first goal is usually to make it work.
But what happens when requirements change?
Initially, our code might work perfectly. But as the application grows, even a small change can become difficult if the code is poorly structured.
So, writing code that works is important, but writing code that is easy to understand and maintain is equally important.
While learning software design, I came across a few principles that help us write better code.
I have already written about the SOLID principles with real-life examples.
You can read that article here:
SOLID Principles Explained Through Real-Life Examples
In this article, I'll cover three more principles:
- DRY: Don't Repeat Yourself
- KISS: Keep It Simple, Stupid
- YAGNI: You Aren't Gonna Need It
1. DRY Principle (Don't Repeat Yourself)
DRY stands for Don't Repeat Yourself.
The idea is to avoid writing the same logic in multiple places.
The Problem: Repeating the Same Logic
Suppose we are developing an online shopping application and need to calculate the final price after applying a discount.
price = 1000
discount = price * 0.10
final_price = price - discount
print(final_price)
Now, suppose we need the same calculation on another page. We write the same logic again.
If the discount changes, we must update both places. If we forget one, the application might calculate different prices.
Applying DRY
Instead, we can create a reusable function.
def calculate_final_price(price):
discount = price * 0.10
return price - discount
print(calculate_final_price(1000))
print(calculate_final_price(2000))
Now, the calculation is written in one place and can be reused wherever needed.
Use Cases of DRY
1. Reusing Common Logic
Suppose we need to check whether a person is an adult in different parts of an application.
Instead of writing the same condition repeatedly, we can create a reusable function.
def is_adult(age):
return age >= 18
print(is_adult(22))
print(is_adult(16))
Now, we can call is_adult() wherever we need to check a person's age.
2. Sharing Common Functionality Through Inheritance
Suppose we have two types of employees: Developers and Testers.
Both have a name and need to display it. However, developers write code, while testers test applications.
Instead of repeating the common functionality, we can put it in a parent class.
class Employee:
def __init__(self, name):
self.name = name
def display_name(self):
print(f"Employee: {self.name}")
class Developer(Employee):
def write_code(self):
print(f"{self.name} is writing code")
class Tester(Employee):
def test_application(self):
print(f"{self.name} is testing the application")
developer = Developer("Anu")
tester = Tester("Ravi")
developer.display_name()
developer.write_code()
tester.display_name()
tester.test_application()
Both classes reuse display_name() from the Employee class, while each class has its own functionality.
3. Using Constants for Repeated Values
Suppose we use the same tax rate in multiple calculations.
Instead of hardcoding the value everywhere, we can define a constant.
TAX_RATE = 0.18
price = 1000
tax = price * TAX_RATE
print(tax)
If the tax rate changes, we can update it in one place.
4. Creating Reusable Modules
Suppose multiple Python files need to calculate the area of a circle.
Instead of writing the same function in every file, we can create a separate module.
In math_utils.py:
import math
def circle_area(radius):
return math.pi * radius ** 2
In another file:
from math_utils import circle_area
print(circle_area(5))
Now, we can reuse the same function in different files.
Benefits of DRY
- Less duplicated code: We avoid repeating the same logic.
- Easier maintenance: We can update shared logic in one place.
- Consistent behavior: The same logic works across different parts of the application.
- Easier testing: We can test reusable functions independently.
Cons
- Over-abstraction: Combining unrelated logic can make code difficult to understand.
- Unwanted dependencies: Changes to shared code can affect multiple parts of the application.
We should avoid repeating the same logic, but we don't need to combine code just because it looks similar.
2. KISS Principle (Keep It Simple, Stupid)
KISS stands for Keep It Simple, Stupid.
The idea is to avoid making code more complicated than necessary.
The Problem
Suppose we want to check whether a number is even.
def is_even(number):
if number % 2 == 0:
return True
else:
return False
But do we really need an if-else statement here?
Applying KISS
We can simplify it:
def is_even(number):
return number % 2 == 0
Both functions produce the same result, but the second one is simpler and easier to read.
How to Apply KISS
- Break large problems into smaller functions.
- Use clear variable and function names.
- Avoid unnecessary complexity.
- Choose straightforward solutions when they meet the requirements.
Benefits of KISS
- Easier to read and understand.
- Easier to debug and maintain.
- Less unnecessary complexity.
- Faster code reviews.
Cons
Oversimplifying code can make it difficult to handle important requirements or edge cases.
Remember, shorter code is not always simpler code.
3. YAGNI Principle (You Aren't Gonna Need It)
YAGNI stands for You Aren't Gonna Need It.
The idea is to build what is required now instead of implementing features just because we might need them in the future.
The Problem: Building Unnecessary Features
Suppose we are developing a payment system that currently supports only credit card payments.
While developing it, we think:
"What if we need PayPal or cryptocurrency payments in the future?"
So, we start implementing support for those payment methods even though nobody has requested them.
This takes extra time and makes the code more complicated.
Applying YAGNI
For now, we should implement only the required credit card payment functionality.
If the requirements change later, we can add the necessary features then.
How to Apply YAGNI
- Understand the current requirements.
- Focus on features that are actually needed.
- Avoid implementing hypothetical features.
- Add functionality when there is a real requirement for it.
Benefits of YAGNI
- Faster development.
- Less unnecessary code.
- Reduced development effort.
- Better focus on current requirements.
Cons
- Future requirements may require refactoring.
- New features may take longer if the existing design needs changes.
YAGNI doesn't mean ignoring future maintainability. It means avoiding unnecessary implementation before it is needed.
The goal is to write code that solves the current problem and is easy to understand and maintain.

Top comments (0)