KISS, DRY, SoC, YAGNI, SOLID: Fix Unhealthy Code
Table of Contents
- Introduction
- KISS: Keep It Simple
- DRY: Do Not Repeat Yourself
- SoC: Separation of Concerns
- YAGNI: You Are Not Gonna Need It
- SOLID: Design Principles for Maintainability
- How to Apply These Principles Gradually
- Conclusion
Introduction
In the previous article about code health, we looked at code that is hard to read, risky to change, and painful to maintain.
This article focuses on the medicine: practical engineering principles that help keep code clean and maintainable. The most common ones are KISS, DRY, SoC, YAGNI, and SOLID.
These principles are not rules to memorize for interviews. They are tools for making better decisions while building software.
KISS: Keep It Simple
KISS means Keep It Simple.
Simple code is easier to read, test, debug, and change. Simple does not mean simplistic. It means the solution is only as complex as the problem requires.
Avoid code like this when a direct version is enough:
const isEligible = user.age >= 18 && user.status === "active";
Do not turn it into a complex abstraction too early:
const isEligible = eligibilityEngine.evaluate(
new UserEligibilityContext(user)
);
Abstractions are useful when they solve real complexity. They are costly when they only make simple logic harder to follow.
DRY: Do Not Repeat Yourself
DRY means Do Not Repeat Yourself.
Duplication is risky because a future change may update one place but forget another. For example, repeated discount logic across controllers, services, and jobs can easily drift apart.
Instead of duplicating:
const finalPrice = price - price * 0.1;
across many files, extract a clear function:
function applyDiscount(price, discountRate) {
return price - price * discountRate;
}
But be careful: not all similar-looking code should be merged immediately. Sometimes two pieces of code look similar today but will evolve differently tomorrow. DRY should reduce meaningful duplication, not create premature coupling.
SoC: Separation of Concerns
Separation of Concerns means each part of the system should focus on a specific responsibility.
For example:
- controllers handle HTTP requests and responses
- services handle business logic
- repositories handle data access
- validators handle input validation
- views handle presentation
When everything is mixed into one file or one function, every change becomes risky.
Instead of putting validation, database queries, payment logic, and response formatting in one controller, separate them into focused units. This makes each part easier to test and reason about.
YAGNI: You Are Not Gonna Need It
YAGNI means You Are Not Gonna Need It.
It warns us not to build features, abstractions, configuration layers, or extension points before there is a real need.
Common YAGNI violations include:
- building a plugin system before there are plugins
- adding multi-tenant architecture for a single-tenant product
- creating five layers for a small CRUD feature
- adding settings nobody uses
- writing generic code for imaginary future requirements
Future-proofing sounds responsible, but unused complexity still has a maintenance cost.
SOLID: Design Principles for Maintainability
SOLID is a group of five object-oriented design principles:
- Single Responsibility Principle: a class should have one reason to change
- Open/Closed Principle: code should be open for extension but closed for modification
- Liskov Substitution Principle: subclasses should be usable wherever the parent class is expected
- Interface Segregation Principle: prefer small focused interfaces over large general ones
- Dependency Inversion Principle: high-level modules should depend on abstractions, not concrete details
SOLID helps when a codebase grows and objects start depending on each other in complex ways. It encourages boundaries, replaceability, and testability.
How to Apply These Principles Gradually
You do not need to rewrite the whole codebase.
Start with small steps:
- Rename unclear variables.
- Extract one long function into smaller functions.
- Move duplicated logic into a shared helper.
- Separate business logic from HTTP handling.
- Delete unused abstraction.
- Add tests before changing risky code.
The best improvements are often small and repeated consistently.
Conclusion
KISS, DRY, SoC, YAGNI, and SOLID are not competing ideas. They work together:
- KISS keeps code understandable.
- DRY reduces meaningful duplication.
- SoC separates responsibilities.
- YAGNI prevents unnecessary complexity.
- SOLID improves long-term maintainability.
Use them as decision-making tools, not as rigid dogma. The goal is code that stays healthy as the product changes.
Related Articles
Keep reading within the same topic.
SOLID Principles: Write Maintainable Software
Understand the five SOLID principles with practical examples so your object-oriented code becomes easier to extend, test, and maintain.
Domain-Driven Design: Structuring Large Codebases
Use Domain-Driven Design to structure large codebases with ubiquitous language, bounded contexts, entities, value objects, aggregates, repositories, and domain services.
Clean Code Habits: 7 Practical Tips for Busy Developers
Adopt practical clean code habits for naming, small functions, refactoring, review, and readability without slowing feature delivery.
Naming Conventions: Variables, Functions, and Classes
A practical guide to naming variables, functions, classes, booleans, API routes, and files so your code is easier to read and maintain.