Logo Gerardo Perrucci - Full Stack Developer

SOLID, Clean Code, DRY, KISS, YAGNI Principles + React

As a software developer , creating code that is easy to maintain, understand, and scale is crucial. Today, we’ll explore five essential principles: SOLID, Clean Code, DRY, KISS, and YAGNI. We will learn these concepts using simple explanations, practical examples in TypeScript, and highlight when and why each principle should be used.

SOLID Principles


SOLID Principles

SOLID is a group of five design principles that help programmers write clearer and better-organized software. Let’s break them down:

Single Responsibility Principle (SRP)

Imagine you have a toy box, and each toy has its own special place. Similarly, in programming, each part of your code should do one specific thing.

Example:

// Bad example: doing two things
class Book {
  getTitle() {}
  saveToDatabase() {}
}

// Good example
class Book {
  getTitle() {}
}

class BookDatabase {
  save(book: Book) {}
}

Open/Closed Principle (OCP)

This means you should add new features without changing the existing ones.

Example:

interface Shape {
  area(): number;
}

class Circle implements Shape {
  constructor(private radius: number) {}
  area() {
    return Math.PI * this.radius ** 2;
  }
}

class Square implements Shape {
  constructor(private side: number) {}
  area() {
    return this.side ** 2;
  }
}

Liskov Substitution Principle (LSP)

If you have a parent class, you should be able to replace it with any child class without breaking the code.

Example:

class Bird {}

class FlyingBird extends Bird {
  fly() {}
}

class Sparrow extends FlyingBird {}
class Penguin extends Bird {}

Interface Segregation Principle (ISP)

Make smaller interfaces that serve specific purposes rather than a single, large interface.

Example:

interface Printer {
  print(): void;
}

interface Scanner {
  scan(): void;
}

Dependency Inversion Principle (DIP)

Depend on abstract classes or interfaces rather than concrete classes.

Example:

interface Database {
  save(data: string): void;
}

class MySQLDatabase implements Database {
  save(data: string) {}
}

class DataService {
  constructor(private database: Database) {}
}

Official SOLID documentation

Clean Code

Clean code is easy to read and maintain. Always write your code clearly and simply.

Example:

// Confusing
function c(x: number[]) {
  return x.reduce((a, b) => a + b, 0);
}

// Clean
function calculateTotal(numbers: number[]) {
  return numbers.reduce((total, number) => total + number, 0);
}

Clean Code GitHub Repository

DRY (Don't Repeat Yourself)

Never write the same code twice. Reuse existing code.

Example:

// Repeated logic
function createUser(user) {
  if (!user.name) throw new Error('Name is required');
}

function updateUser(user) {
  if (!user.name) throw new Error('Name is required');
}

// DRY approach
function validateUser(user) {
  if (!user.name) throw new Error('Name is required');
}

KISS (Keep It Simple, Stupid)

Always choose simplicity over complexity.

Example:

// Complex
function multiply(a, b) {
  return Array(b)
    .fill(a)
    .reduce((sum, val) => sum + val, 0);
}

// Simple
function multiply(a, b) {
  return a * b;
}

YAGNI (You Aren't Gonna Need It)

Don't add features until you really need them.

Example:

// Over-engineering
class User {
  login() {}
  logout() {}
  sendEmail() {}
}

// YAGNI
class User {
  login() {}
  logout() {}
}
// Add sendEmail() only if needed later.

Comparative Table

PrincipleProsCons
SOLIDHighly maintainable, organized codeMay initially feel complex
Clean CodeEasy to read, maintainRequires discipline
DRYReduces repetitionCan lead to overly complex abstractions
KISSEasy to debug, straightforwardCan oversimplify solutions
YAGNIReduces wasted effortCan overlook future scalability

How to Apply These Principles to React Components

Applying SOLID, Clean Code, DRY, KISS, and YAGNI principles in React enhances maintainability, readability, and scalability of your components. Here’s how each principle can specifically benefit React development:

SOLID Principles in React

  • Single Responsibility Principle (SRP): React components should have a single, clear purpose.
// Violation
function Profile({ user, onLogout }) {
  return (
    <div>
      <p>{user.name}</p>
      <button onClick={onLogout}>Logout</button>
      <button>Edit Profile</button>
    </div>
  );
}

// Adhering to SRP
function Profile({ user }) {
  return <p>{user.name}</p>;
}

function LogoutButton({ onLogout }) {
  return <button onClick={onLogout}>Logout</button>;
}
  • Open/Closed Principle (OCP): Use component composition instead of modifying existing components.
function Button({ children, variant }) {
  return <button className={`btn-${variant}`}>{children}</button>;
}

// Extending component without modification
function IconButton({ icon, children }) {
  return (
    <Button variant="icon">
      {icon} {children}
    </Button>
  );
}
  • Liskov Substitution Principle (LSP): Sub-components should seamlessly replace base components.
function Alert({ message }) {
  return <div className="alert">{message}</div>;
}

function SuccessAlert(props) {
  return <Alert {...props} className="alert-success" />;
}
  • Interface Segregation Principle (ISP): Props should be clearly separated by responsibilities.
// Violation
function DataDisplay({ data, loading, error }) {}

// Adhering to ISP
function DataView({ data }) {}
function LoadingSpinner() {}
function ErrorDisplay({ error }) {}
  • Dependency Inversion Principle (DIP): Inject dependencies via props or context.
function UserProfile({ fetchUser }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser().then(setUser);
  }, [fetchUser]);

  return <div>{user?.name}</div>;
}

Clean Code in React

Write concise, well-named components, and avoid deeply nested structures:

// Clean Code
function UserList({ users }) {
  return users.map(user => <UserCard key={user.id} user={user} />);
}

DRY in React

Extract reusable logic into custom hooks or higher-order components:

function useFetch(url) {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(setData);
  }, [url]);
  return data;
}

function UserProfile({ userId }) {
  const user = useFetch(`/api/users/${userId}`);
  return <div>{user?.name}</div>;
}

KISS in React

Simplify your component logic and avoid premature optimization:

// KISS Violation
const [state, dispatch] = useReducer(complexReducer, initialState);

// KISS Approach
const [count, setCount] = useState(0);

YAGNI in React

Implement only what your components need today; extend them later:

// YAGNI Violation
function Button({ variant, size, disabled, loading }) {}

// YAGNI Approach
function Button({ children, onClick }) {}
// Add props later as requirements evolve

When to Use Each Principle?

  • SOLID: Large and complex applications needing structure and scalability.
  • Clean Code: Every project to ensure readability.
  • DRY: Applications with repetitive logic.
  • KISS: Quick prototyping and simple applications.
  • YAGNI: Agile environments or startup projects with changing requirements.

SEO Keywords

  • SOLID principles
  • Clean Code
  • DRY principle
  • KISS principle
  • YAGNI

Sources