As developers, writing code that works is just the beginning. Writing code that is maintainable, scalable, and clean is what separates good engineers from great ones. These principles come up constantly during code reviews, and understanding them will make you both a better author and reviewer.

These four principles - SOLID, DRY, KISS, and YAGNI - are foundational concepts that every developer should understand and apply in their daily work.

SOLID Principles

SOLID is an acronym for five object-oriented design principles that help developers write more maintainable and flexible code.

S - Single Responsibility Principle

A class should have one, and only one, reason to change. Each module or class should be responsible for a single part of the functionality.

// Bad - one class doing too much
class UserManager {
  authenticateUser() { }
  sendEmail() { }
  generateReport() { }
}

// Good - separated responsibilities
class AuthService { authenticateUser() { } }
class EmailService { sendEmail() { } }
class ReportService { generateReport() { } }

O - Open/Closed Principle

Software entities should be open for extension, but closed for modification. You should be able to add new functionality without changing existing code.

L - Liskov Substitution Principle

Objects of a superclass should be replaceable with objects of a subclass without breaking the application. If S is a subtype of T, then objects of type T can be replaced with objects of type S.

I - Interface Segregation Principle

A client should never be forced to implement an interface it doesn't use. Many specific interfaces are better than one general-purpose interface.

D - Dependency Inversion Principle

High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details - details should depend on abstractions.

DRY - Don't Repeat Yourself

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

DRY is about reducing repetition in code. When you find yourself copying and pasting code, that's a sign you should extract it into a reusable function or module.

// Bad - repeated logic
function validateEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

function validateAdminEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email) && email.endsWith('@company.com');
}

// Good - reuse the validation
function validateEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

function validateAdminEmail(email) {
  return validateEmail(email) && email.endsWith('@company.com');
}

However, be careful not to over-apply DRY. Sometimes a little repetition is better than the wrong abstraction.

KISS - Keep It Simple, Stupid

Most systems work best if they are kept simple rather than made complicated. Simplicity should be a key goal in design, and unnecessary complexity should be avoided.

The KISS principle states that:

  • Write code that is easy to understand
  • Avoid clever tricks that make code hard to read
  • Use straightforward solutions over complex ones
  • If a junior developer can't understand your code, simplify it
// Clever but hard to read
const r = a.filter(x => x%2).reduce((s,x) => s+x, 0);

// Simple and clear
const oddNumbers = numbers.filter(n => n % 2 !== 0);
const sum = oddNumbers.reduce((total, n) => total + n, 0);

YAGNI - You Aren't Gonna Need It

Don't implement something until it is necessary. YAGNI is about avoiding over-engineering. Don't add functionality just because you think you might need it in the future.

Common YAGNI violations:

  • Building a plugin system when you only need one implementation
  • Adding configuration options nobody asked for
  • Creating abstract base classes for things that only have one subclass
  • Designing for "future requirements" that may never come

The cost of building something you don't need:

  • Time spent building it
  • Time spent testing it
  • Time spent maintaining it
  • Complexity added to the codebase

How These Principles Work Together

These principles are complementary:

  • SOLID gives you the architecture guidelines
  • DRY tells you when to extract and reuse
  • KISS reminds you to keep it simple
  • YAGNI tells you when to stop

The best code follows all four: it has clear responsibilities (SOLID), minimal repetition (DRY), straightforward logic (KISS), and no unnecessary features (YAGNI). This is also worth keeping in mind when evaluating AI-generated code, which often violates YAGNI by over-engineering solutions. For a deeper dive into refactoring patterns that align with these principles, Refactoring Guru is an excellent resource.

Further Reading