Skip to content
Dev Dump

Design Principles

To build software that scales without collapsing under its own weight, engineers rely on core acronyms that dictate architectural philosophy. These principles act as guiding lights during code reviews and system design.

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

Code duplication is the enemy of maintainability. If logic is duplicated in three places, updating that logic requires remembering to change it in three places. If you forget one, you introduce a bug.

Violating DRY:

class Employee {
public double calculateBonus() {
return salary * 0.1; // Bonus logic here
}
}
class Manager extends Employee {
public double calculateBonus() {
return getSalary() * 0.1; // Duplicated logic here
}
}

Following DRY: Move the shared logic into a base method or a dedicated calculator class, and reuse it.


Most systems work best if they are kept simple rather than made complicated.

Do not use advanced language features, complex design patterns, or nested ternary operators just to “look smart.” Simple code is easy to read, easy to test, and easy to fix.

Violating KISS:

// Overly complex boolean logic
if (isLoggedIn && (role.equals("ADMIN") || role.equals("MOD")) && !isBanned) {
grantAccess();
}

Following KISS:

// Self-documenting variables
boolean hasPrivileges = isAdmin() || isModerator();
boolean canAccess = isLoggedIn && hasPrivileges && !isBanned;
if (canAccess) {
grantAccess();
}

Always implement things when you actually need them, never when you just foresee that you need them.

YAGNI Illustration Don’t build features based on hypotheticals. It creates dead code and slows down development.

Engineers love to over-engineer. Building a complex plugin architecture for an app that currently only needs to connect to one database violates YAGNI. Write code for the requirements of today.


A system should be divided into distinct, independent sections, where each section addresses a separate concern.

This is the architectural equivalent of the Single Responsibility Principle. Don’t mix database queries with HTML rendering.

Violating SoC:

class UserManager {
public void saveUser(User user) {
// SQL query here
}
public void sendWelcomeEmail(User user) {
// SMTP logic here
}
}

Following SoC: Split into UserRepository (database concern) and EmailService (notification concern).


Achieve code reuse by assembling (composing) smaller, independent objects rather than inheriting from rigid parent classes.

Inheritance creates a rigid “Is-A” relationship. Composition creates a flexible “Has-A” relationship.

Composition over Inheritance Inheritance forces tight coupling. Composition allows you to swap behaviors (engines) dynamically.

Violating the Principle (Inheritance):

class Engine { public void start() { } }
// A Car IS NOT an Engine. This is semantically wrong.
class Car extends Engine {
public void drive() {
start();
}
}

Following the Principle (Composition):

// A Car HAS AN Engine.
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine; // Can inject any type of Engine!
}
public void drive() {
engine.start();
}
}

6. Program to an Interface, Not an Implementation

Section titled “6. Program to an Interface, Not an Implementation”

Depend on abstractions (interfaces), not on concrete classes.

This principle allows you to swap out implementations without touching the higher-level code.

Violating the Principle:

// Tightly coupled to a specific class
ArrayList<String> list = new ArrayList<>();

Following the Principle:

// Programmed to an interface.
// Can easily swap ArrayList for LinkedList later.
List<String> list = new ArrayList<>();