In the world of software, where every line of code can be the “seed” for a robust system or a “time bomb” of errors, we — the developers — need more than just the ability to write working code. We need to write code that is readable, maintainable, and won’t become a burden in the future.
The three principles KISS, DRY, and YAGNI are a “guiding trio” that have existed and proven their value over decades. They are not just empty slogans, but the distilled essence of hard-earned experience from countless successful software projects… and failures as well.
In this article, we will delve into the meaning, mindset behind, and practical application of these three principles. Consider this not just as knowledge, but as the survival philosophy of a programmer.
1. Overview
KISS, DRY, and YAGNI are commonly referred to as “software design principles” or more specifically as “programming principles” (programming principles / coding principles).
They belong to the “best practices” group – methods and principles widely recognized to help make code more maintainable, scalable, and error-resistant.

In addition to these three principles, developers often refer to other principles such as:
- SOLID – 5 object-oriented design principles.
- GRASP – Principles of responsibility assignment in OOP.
- SoC (Separation of Concerns) – Separate functionalities for easier management.
- SNE (Simple, Not Easy) – similar to KISS but focuses on “simplicity without compromising quality.”
- Occam’s Razor – Choose the simplest solution possible that still meets the requirements.
- Fail Fast – Detect errors as early as possible to reduce repair costs.
In this article, we will focus primarily on KISS, DRY, and YAGNI.
2. KISS — Keep It Simple, Stupid

2.1 Definition & Philosophy
KISS is a reminder to: Keep everything as simple as possible. In system design and coding, “simple” does not mean removing features, but rather avoiding unnecessary complexity.
Complex systems often arise from the accumulation of small but uncontrolled decisions. A function that was originally just 10 lines can, after several “patches,” turn into 200 lines with many branches, forcing the reader to spend hours trying to understand it.
2.2 Why is KISS important?
- Speed of understanding and maintenance: Simple code helps new developers joining the project quickly grasp it.
- Reduced risk of errors: Every redundant piece of logic is an opportunity for a bug to appear.
- Optimized scalability: A simple platform makes it easier to add features compared to a tangled one.
2.3 Principles of application
- Always ask: “Is there a shorter and clearer way?”
- Avoid “premature optimization” (premature optimization).
- Use familiar structures and libraries instead of “inventing” things that no one understands.
2.4 Example
Problem: Calculate the total order value (including discount and tax)
You have a list of products in the cart, each product has:
price
: pricequantity
: quantity- May have
discount
(optional) - VAT is 10%
Goal: Calculate the total amount the customer has to pay.
❌ Complex approach, violating KISS
def calculate_total(cart):
total = 0
for i in range(len(cart)):
product = cart[i]
price = product["price"]
quantity = product["quantity"]
discount = 0
if "discount" in product:
discount = product["discount"]
subtotal = price * quantity
if discount > 0:
subtotal = subtotal - (subtotal * discount)
tax = subtotal * 0.1
subtotal = subtotal + tax
total = total + subtotal
return total
Problem:
- Too many unnecessary temporary variables.
- Too many sequential steps, making it hard to see the “big picture.”
- The loop using
range(len(cart))
is unnecessary. - Discount and tax logic is scattered, making it hard to reuse.
✅ A more KISS-friendly approach
def calculate_total(cart):
def item_total(p):
subtotal = p["price"] * p["quantity"]
subtotal *= (1 - p.get("discount", 0))
return subtotal * 1.1 # add 10% VAT
return sum(item_total(product) for product in cart)
Why is it simpler?
- Clear intention: At a glance, it’s clear — “calculate each product’s total and then sum them up.”
- Fewer temporary variables: No need to store each intermediate value if it’s only used once.
- Small function extraction:
item_total
helps encapsulate the logic for calculating each product’s price. - Easy maintenance: If the tax later changes from 10% → 8%, you only need to update it in one place.
Lesson from this example
- KISS is not just about “writing fewer lines” but about keeping the ideas in the code clear and easy to read.
- A newcomer to the project reading a KISS-style function will immediately understand its purpose and flow, without having to “translate” each variable one by one.
- KISS code usually has fewer bugs because there are fewer points of confusion.
3. DRY — Don’t Repeat Yourself

3.1 Definition & Philosophy
DRY advocates: Don’t Repeat Yourself. If a piece of logic appears in multiple places, you are doubling the maintenance workload and doubling the risk of errors.
The philosophy of DRY lies in this: Each idea, each piece of knowledge in the system should exist in only one place.
3.2 Why is DRY important?
- Easy maintenance: When changes are needed, you only have to update it in one place.
- Fewer errors: No worries about fixing it in one file but forgetting in another.
- Reusability: Common logic is consolidated into a function, module, or class, helping keep the project consistent.
3.3 Principles of application
- When you find yourself copy-pasting code from one file to another, stop and think about how to extract it into a function or module.
- Use inheritance or composition in OOP when appropriate.
- Use constants or a config file instead of hard-coding values multiple times.
2.4 Example
Problem: Employee management and payroll calculation
You need to write a small system to:
- Store employee information.
- Calculate net salary based on base salary, bonuses, and taxes.
❌ Approach violating DRY (repeating the same logic in multiple places)
def calculate_salary(emp):
gross_salary = emp["base_salary"] + emp["bonus"]
tax = gross_salary * 0.1
net_salary = gross_salary - tax
return net_salary
def print_salary(emp):
gross_salary = emp["base_salary"] + emp["bonus"]
tax = gross_salary * 0.1
net_salary = gross_salary - tax
print(f"{emp['name']} receives {net_salary} VND")
Problem:
- The salary calculation logic (
gross_salary
,tax
,net_salary
) is repeated in both functions. - If the tax needs to change from 10% → 8%, you have to update multiple places, which is easy to forget.
- Increases the risk of bugs if updates are missed.
✅ DRY approach (consolidate common logic in one place)
def net_salary(emp):
"""Calculate net salary (applying DRY)"""
gross_salary = emp["base_salary"] + emp["bonus"]
tax = gross_salary * 0.1
return gross_salary - tax
def calculate_salary(emp):
return net_salary(emp)
def print_salary(emp):
print(f"{emp['name']} receives {net_salary(emp)} VND")
Why is it better?
- Logic tính lương chỉ tồn tại ở một hàm duy nhất (
net_salary
). - If the tax changes, you only need to update one place.
- Other functions simply call the common function, without copy-pasting.
Lesson from this example
- DRY not only makes the code cleaner but also protects you from “missing updates” bugs.
- The DRY principle is especially important in large projects, where a piece of logic can appear in dozens of different files.
- DRY = “Write once, use many times” → saves time and reduces risk.
4. YAGNI — You Aren’t Gonna Need It

4.1 Definition & Philosophy
YAGNI is a warning: Don’t build things you don’t need yet. Many developers tend to “anticipate” features thinking “they will be needed later,” but in reality, many of these “anticipatory features” are never used.
4.2 Why is YAGNI important?
- Save time: Focus on doing things that currently bring value.
- Reduce risk: Every line of code is a potential place for bugs; if it’s not necessary yet, it’s better not to write it.
- Avoid overcomplicating the system: Redundant features make it harder for others to understand the code.
4.3 Principles of Application
- Only implement features when there is a clear request from the product team or the customer.
- If future expansion is needed, design the code to be easily extendable rather than pre-writing everything.
- Focus on the MVP (Minimum Viable Product) first.
4.4 Examples
Context
Suppose you are assigned the task of writing a function to calculate the sum of integers in a list
.
You predict that in the future it might need to support decimals, complex numbers, or handle nested lists… so you immediately write an “ultra-versatile” version right from the start.
❌ An approach that violates YAGNI
def sum_list(lst, allow_float=False, allow_complex=False, recursive=False):
total = 0
for item in lst:
if isinstance(item, int):
_
Problem:
- You added many unnecessary features (float, complex, recursive).
- The code becomes harder to read and maintain.
- There’s a chance the extensions may never be used, wasting development and testing time.
✅ An approach that follows YAGNI
def sum_list(lst):
total = 0
for item in lst:
if isinstance(item, int):
total += item
return total
print(sum_list([1, 2, 3])) # 6
Explanation:
- You only write what is necessary for the current requirement.
- If one day you truly need to add decimals or nested lists, then you extend it.
- Helps keep the code clean, easy to understand, and saves development time.
Lesson from YAGNI:
Don’t “predict” the future when writing code. Over-anticipating needs can make software more complex and time-consuming without necessarily being used. Focus on addressing the current requirements and extend when needed.
5. The Relationship between KISS, DRY, and YAGNI
These three principles do not exist in isolation; they complement each other:
- KISS keeps the code simple, making DRY and YAGNI easier to apply.
- DRY keeps the system tidy, which in turn helps maintain KISS.
- YAGNI keeps the codebase lean, making KISS and DRY achievable.
If programming is compared to building a house:
- KISS = a clear, uncluttered blueprint.
- DRY = reusing materials and techniques instead of creating everything from scratch.
- YAGNI = not building a swimming pool when no one wants to swim yet.
6. Common Mistakes When Applying
- KISS but overly simplified
- Wrong: Removing important features just to make the code shorter.
- Right: Keep the necessary features, but avoid adding unnecessary complexity.
- DRY but applied excessively
- Wrong: Forcing everything into a single function, making the code hard to read.
- Right: Group only the truly common parts, maintaining balance.
- YAGNI but ignoring potential future extensions
- Wrong: Ignoring the possibility of future extensions, leading to a complete rewrite later.
- Right: Design for extensibility but avoid writing unnecessary code.
7. Conclusion
The three principles KISS, DRY, and YAGNI form the foundation of professional programming mindset.
- KISS reminds you to keep everything clear and avoid unnecessary complexity.
- DRY ensures you don’t repeat yourself, reducing risk and improving maintainability.
- YAGNI helps you focus on current value, avoiding waste on things that might never be used.
Before committing code, ask yourself:
- KISS — Is there a simpler way to do this?
- DRY — Am I repeating myself anywhere?
- YAGNI — Am I doing something unnecessary?
Programming is not just about “making the computer run,” but about writing code that humans can understand and computers can execute efficiently. These three principles are the bridge between art and engineering in programming.
Read more:
- Read more:
What is SOLID? Principles and how to apply them in practice - Clean Code: The Art of Writing Clean and Sustainable Code in Software Development
References
- The Elements of Programming Style” – Brian W. Kernighan & P.J. Plauger
- “The Pragmatic Programmer” – Andrew Hunt & David Thomas (1999)
- “Extreme Programming Explained” – Kent Beck (1999)
- “DRY Principle” – ThoughtWorks Insights
- “YAGNI” – Martin Fowler