
Software Design Principles (Basics) : DRY, YAGNI, KISS, etc
- Sudhir mangla
- Design Principles , Clean Code
- 14 Mar, 2025
As a software architect beginning your journey with Microsoft technologies—especially C# and .NET—you’re probably already realizing that building software isn’t just about writing code; it’s about writing code that’s easy to maintain, scale, and understand. But how do you get there?
The secret lies in mastering fundamental software design principles. These are the building blocks of good software. In this guide, we’ll explore principles like:
- Don’t Repeat Yourself (DRY)
- Curly’s Law (Do One Thing)
- Keep It Simple, Stupid (KISS)
- You Aren’t Gonna Need It (YAGNI)
- Premature Optimization is the Root of All Evil
- Boy-Scout Rule
- Code for the Maintainer
- Principle of Least Astonishment (POLA)
Master these, and you’ll build clean, robust software in no time.
1. Don’t Repeat Yourself (DRY)
What’s DRY All About?
Have you ever read a book that repeats the same point over and over? Annoying, isn’t it? The same applies to software code. DRY means every important concept or logic in your software should exist only once. If you copy-paste the same code everywhere, you’re setting yourself up for trouble.
Why Should You Care About DRY?
Duplicated code invites errors. Change something in one place and forget another—suddenly your app behaves unpredictably.
DRY Principle in Action (C# example):
Let’s start with what you shouldn’t do:
// Violating DRY principle
public void SendEmailToAdmin(string message)
{
var adminEmail = "admin@example.com";
EmailService.Send(adminEmail, message);
}
public void NotifyAdmin(string notification)
{
var adminEmail = "admin@example.com";
NotificationService.Notify(adminEmail, notification);
}
Notice how “admin@example.com” is duplicated. Imagine updating it everywhere. Nightmare, right?
Here’s how DRY saves the day:
// DRY-compliant implementation
private const string AdminEmail = "admin@example.com";
public void SendEmailToAdmin(string message)
{
EmailService.Send(AdminEmail, message);
}
public void NotifyAdmin(string notification)
{
NotificationService.Notify(AdminEmail, notification);
}
Now, if the admin’s email changes, you only update it once. DRY keeps your code neat and bug-free.
2. Curly’s Law - Do One Thing Well
What is Curly’s Law (Single Responsibility Principle)?
Ever tried using a Swiss Army knife? It’s versatile, but each tool isn’t as good as a dedicated tool designed for one specific task. Curly’s Law (also known as Single Responsibility Principle) says each piece of your code should have one clear responsibility.
Why Focus on Just One Thing?
When code tries to do multiple things simultaneously, it becomes messy. Debugging or adding new features turns into a nightmare. One responsibility per component makes everything simpler.
Curly’s Law in C#:
Bad Example:
// Too many responsibilities in one place
public void RegisterUser(User user)
{
ValidateUser(user);
SaveUserToDatabase(user);
SendConfirmationEmail(user);
}
What if validation logic changes? You’ll have to rewrite code handling emails and database saves too!
Good Example (Improved):
// Refactored for Curly's Law compliance
public void RegisterUser(User user)
{
if(UserValidator.IsValid(user))
{
UserRepository.Save(user);
EmailNotifier.SendConfirmation(user);
}
}
Clearly defined tasks mean faster changes, fewer bugs, and easier testing.
3. Keep It Simple, Stupid (KISS)
Why Simple Beats Complex Every Time
Have you noticed how complex machinery tends to break down more easily than simpler tools? KISS tells you clearly: keep it simple. The simpler the code, the easier it is to understand, test, and fix.
KISS Principle Illustrated (C# example):
Unnecessarily complex:
// Overly complicated implementation
public int Add(int a, int b)
{
return (int)Math.Round(Math.Exp(Math.Log(a) + Math.Log(b)));
}
Clear and simple:
// Simple implementation
public int Add(int a, int b)
{
return a + b;
}
Don’t complicate things. Simplicity always wins.
4. You Aren’t Gonna Need It (YAGNI)
Ever Packed Extra Clothes “Just in Case”?
Most of us tend to overprepare, packing clothes we never end up wearing. Software developers do this too—coding features they’ll “maybe need later.” YAGNI helps you stay lean: Don’t build something until you actually need it.
YAGNI in Action (C# example):
Premature abstraction example:
// Premature abstraction
interface IPaymentGateway
{
void ProcessPayment();
void Refund();
void Cancel();
}
class PayPalGateway : IPaymentGateway
{
// unnecessary implementations for refund and cancel before needed
}
Instead, build just what’s immediately necessary:
// Focused and relevant to immediate needs
interface IPaymentGateway
{
void ProcessPayment();
}
class PayPalGateway : IPaymentGateway
{
public void ProcessPayment() { /* implementation */ }
}
You’ll save time and reduce complexity. You can always expand later.
5. Premature Optimization is the Root of All Evil
Definition: Avoid spending time optimizing before you clearly identify performance bottlenecks.
Why Wait on Optimization?
Ever spent hours fine-tuning something that ultimately didn’t matter? Optimizing too early is like painting your house before it’s built—pointless and messy. Always build clearly first, measure performance second, and optimize only when truly necessary.
Practical Example in C#:
Rather than prematurely optimizing database queries or methods:
- First, ensure the code is clear and correct.
- Then, measure and profile performance.
- Optimize only genuine bottlenecks.
Key Benefits:
- Prioritize correctness over speed initially.
- Reduce wasted effort.
- Maintain readable and maintainable code.
6. Boy-Scout Rule
Always Leave Things Cleaner Than You Found Them
Just like scouts tidy up their campsite, whenever you work with code, make tiny improvements. A small cleanup today prevents a big mess tomorrow.
C# Boy-Scout Rule Example:
Before:
if(user != null && user.IsActive && user.HasPermissions) { ... }
After improvement:
bool hasAccess = user?.IsActive == true && user.HasPermissions;
if(hasAccess) { ... }
Small refinements consistently applied lead to a more manageable and pleasant codebase.
7. Code for the Maintainer
Will Future You Appreciate This?
Think about your code like a note left for your future self. Will it be clear? Or will it leave you scratching your head? Write code that’s easy for anyone—even yourself months later—to understand instantly.
Maintainable Code (C# example):
public bool IsEligibleForDiscount(Customer customer)
{
bool hasMinimumPurchases = customer.PurchaseHistory.Total >= MinimumThreshold;
bool isLoyalCustomer = customer.AccountAgeInYears >= LoyaltyThreshold;
return hasMinimumPurchases && isLoyalCustomer;
}
Good naming, clear structure, and simplicity keep you and your team productive.
8. Principle of Least Astonishment (POLA)
Why Avoid Surprises?
Ever tried driving a friend’s car and realized the controls were completely different from your own vehicle? Software can do this too, surprising users and developers with unexpected behaviors. POLA means your code should always behave predictably.
POLA in C#:
Surprising behavior:
// Astonishing behavior
public double Divide(int a, int b)
{
return b == 0 ? 0 : a / b;
}
This surprises developers who expect clear errors. Instead, do this:
// Predictable behavior using POLA
public double Divide(int a, int b)
{
if(b == 0)
throw new DivideByZeroException("Cannot divide by zero.");
return (double)a / b;
}
Now, your code behaves exactly as others expect—no nasty surprises.
Conclusion
By mastering these fundamental software design principles—DRY, Curly’s Law, KISS, YAGNI, avoiding premature optimization, the Boy-Scout Rule, coding for maintainers, and POLA—you’ll create software that’s easy to build, understand, and extend.
Embrace these ideas, apply them consistently, and you’ll see your skills skyrocket. Software architecture doesn’t have to be complex or intimidating—it can be elegant, logical, and enjoyable.
Ready to start building amazing software with confidence? Dive in and start coding smarter today!