Skip to content
Type something to search...
Software Design Principles (Basics) : DRY, YAGNI, KISS, etc

Software Design Principles (Basics) : DRY, YAGNI, KISS, etc

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!

Related Posts

SOLID Design Principles: A Beginner’s Guide to Clean Software Architecture

SOLID Design Principles: A Beginner’s Guide to Clean Software Architecture

Introduction: What's the Deal with SOLID, Anyway? Have you ever found yourself swimming through layers of tangled code, desperately trying to patch up your application, only to see it fall a

Read More
Clean Code: Best Practices Every Software Architect Should Master

Clean Code: Best Practices Every Software Architect Should Master

As a software architect or developer, have you ever stared at a screen filled with code, only to feel overwhelmed by its complexity? Have you ever questioned if your code could be simpler, clearer, or

Read More
Event-Driven Architecture for Beginners: .NET and C# Examples

Event-Driven Architecture for Beginners: .NET and C# Examples

So, you've heard about Event-Driven Architecture (EDA), and now you're probably wondering—what's all the fuss about? Maybe you've been coding away in C# and .NET, building traditional apps, but

Read More
Layered Architecture Explained: Building Rock-Solid .NET Applications

Layered Architecture Explained: Building Rock-Solid .NET Applications

Introduction: Layers? Why Should I Care? Ever eaten a tasty lasagna? Sure, you have! It has layers—cheese, sauce, pasta—and each layer has its own delicious job. Software architecture isn't much

Read More