Skip to content
Go back

Core Software Design Principles: Foundations for Maintainable Code

Published:  at  12:00 AM

Core Software Design Principles: Foundations for Maintainable Code

As projects grow, the challenge isn’t writing code—it’s maintaining it. Code becomes hard to change, one modification cascades through multiple files, and newcomers can’t find where the rules live. These problems almost always stem from a lack of clear, consistent design principles. This post explores five core principles—SSOT, DRY, KISS, YAGNI, and SRP—that form the foundation of maintainable software.


1. Principles at a Glance

PrincipleFocusProblem Solved
SSOTSingle source of truthPrevent inconsistent logic
DRYAvoid repeated implementationReduce duplicate code
KISSKeep it simplePrevent over-engineering
YAGNIDon’t implement prematurelyAvoid unnecessary complexity
SRPSingle responsibilityReduce modification cost

2. SSOT: Single Source of Truth

Core Idea

Every piece of knowledge must have a single, authoritative source.

SSOT focuses on data consistency and rule ownership, ensuring each “truth” in the system has exactly one definition.

Common Scenarios

Anti-pattern:

// Frontend hardcodes role list
const roles = ["admin", "user", "guest"];

// Backend also defines its own
enum Role {
  ADMIN,
  USER,
  GUEST,
}

Better:

// Backend API returns roles, frontend uses directly
const roles = await fetchRoles();

3. DRY: Don’t Repeat Yourself

Core Idea

The same knowledge should be expressed in exactly one place.

DRY focuses on code structure and logic reuse, eliminating copy-paste patterns.

Common Scenarios

Anti-pattern:

// Permission check duplicated in multiple places
if (user.role === "admin") {
  /* ... */
}
// In another file
if (currentUser.role === "admin") {
  /* ... */
}

Better:

// Extract to a utility function
function isAdmin(user: User): boolean {
  return user.role === "admin";
}

SSOT vs DRY

DimensionSSOTDRY
FocusSource of truthImplementation
ProblemMultiple versions of truthDuplicate code
LevelArchitecture/data layerCode layer

4. KISS: Keep It Simple, Stupid

Core Idea

If a simple solution works, don’t complicate it.

KISS, coined by engineer Kelly Johnson, emphasizes simplicity above all.

Warning Signs

When you see naming like this, you’ve probably overthought it:

AbstractFactoryStrategyManagerProviderFactory;

Practical Guidelines

Anti-pattern:

// Over-engineered: complex pattern for simple config
class ConfigurationStrategyFactory {
  createStrategy(type: string): ConfigStrategy {
    /* ... */
  }
}

Better:

// Simple and direct
const config = {
  apiUrl: process.env.API_URL,
  timeout: 5000,
};

5. YAGNI: You Aren’t Gonna Need It

Core Idea

Don’t implement features you don’t need yet.

Common Pitfalls

The result: complexity explodes, and when the real requirement arrives, the original design doesn’t fit anyway.

Anti-pattern:

// Premature optimization: project has single data source
class MultiDatabaseConnectionPool {
  private pools: Map<string, ConnectionPool>;
  // Complex multi-database management logic...
}

Better:

// Meet current requirements
const db = new Database(process.env.DATABASE_URL);

6. SRP: Single Responsibility Principle

Core Idea

A module or class should have only one reason to change.

Litmus Test

If describing a module’s functionality requires “and” to connect multiple things, it likely violates SRP.

Anti-pattern:

class UserService {
  login(credentials: Credentials) {
    /* ... */
  }
  validatePassword(password: string) {
    /* ... */
  }
  sendWelcomeEmail(user: User) {
    /* ... */
  }
  generateReport(user: User) {
    /* ... */
  }
}

Better:

class AuthService {
  login(credentials: Credentials) {
    /* ... */
  }
  validatePassword(password: string) {
    /* ... */
  }
}

class NotificationService {
  sendWelcomeEmail(user: User) {
    /* ... */
  }
}

class ReportService {
  generateUserReport(user: User) {
    /* ... */
  }
}

7. How These Principles Relate

These principles aren’t isolated—they form a hierarchical constraint system:

┌─────────────────────┐
│        SRP          │  ← Define responsibility boundaries
└──────────┬──────────┘

┌─────────────────────┐
│        DRY          │  ← Reuse within boundaries
└──────────┬──────────┘

┌─────────────────────┐
│       SSOT          │  ← Determine authority
└──────────┬──────────┘

┌─────────────────────┐
│    KISS + YAGNI     │  ← Constrain overall complexity
└─────────────────────┘

SRP defines boundaries → DRY enables reuse → SSOT establishes authority → KISS/YAGNI control complexity


8. Engineering Checklist

Turn these principles into daily checkpoints:


9. Further Reading


These principles aren’t dogma—they’re engineering wisdom validated over decades. Understanding their spirit matters more than memorizing the rules.



Next
The Unix Design Philosophy: Timeless Principles for Modern Software