Systems Design Basics

Systems Design

Low-level and high-level design at campus depth: SOLID with clean code, the six GoF patterns they ask, four classic LLD problems, the HLD blocks behind every system, and full walkthroughs.

S — Single Responsibility

One class, one reason to change.

Violated: UserService validates input, computes billing, emails users and writes audit logs — every new requirement edits the same class, and a bug anywhere breaks everything.

Applied: Split by reason-to-change: UserValidator, InvoiceCalculator, Mailer, AuditLog — each evolves independently and tests in isolation.

Java
// Java
interface Mailer { void send(String to, String body); }

class SmtpMailer implements Mailer {
  public void send(String to, String body) { /* SMTP logic */ }
}

class UserService {
  private final Mailer mailer;
  UserService(Mailer m) { this.mailer = m; }
  void register(User u) {
    // validation + persistence only
    mailer.send(u.email(), "Welcome!");
  }
}

O — Open/Closed

Open for extension, closed for modification — add new behaviour by adding code, not editing proven code.

Violated: An `if (type == "credit") ... else if (type == "debit")` chain grows every payment season, forcing retests of the whole switch.

Applied: Define a PaymentMethod interface; new methods register as new classes. Existing flow untouched.

Python
# Python
class PaymentMethod(Protocol):
    def charge(self, amount: int) -> str: ...

class Upi:
    def charge(self, amount): return f"UPI {amount}"

class Card:
    def charge(self, amount): return f"CARD {amount}"

def checkout(method: PaymentMethod, amount: int):
    return method.charge(amount)   # new types plug in without edits

L — Liskov Substitution

Any subclass must be usable wherever its parent is expected — without surprising behaviour.

Violated: A `Square extends Rectangle` that forces width == height breaks every method that sets them independently; a `Penguin extends Bird` that throws in fly() violates callers' expectations.

Applied: Model the real contract: `FlyingBird` vs `Bird`, or prefer composition. Subclasses strengthen, never weaken, pre/post-conditions.

C++
class Bird {
public:
  virtual ~Bird() = default;
  virtual void eat() = 0;      // all birds eat
};
class FlyingBird : public Bird {
public:
  virtual void fly() = 0;      // only flyers fly
};
class Penguin : public Bird {
  void eat() override { /* fish */ }
  // no fly() to break — hierarchy models reality
};

I — Interface Segregation

Many small, client-specific interfaces beat one fat interface.

Violated: `Worker` with work(), eat(), sleep() forces a RobotWorker to implement eat() and sleep().

Applied: Split into Workable and Restable; robots implement only Workable.

Java
interface Workable { void work(); }
interface Restable { void rest(); }

class Human implements Workable, Restable {
  public void work() { /* ... */ }
  public void rest() { /* ... */ }
}
class Robot implements Workable {
  public void work() { /* 24/7 */ }
}

D — Dependency Inversion

High-level policy depends on abstractions, not concrete low-level details.

Violated: OrderService news up MySQLDatabase directly — switching to Postgres means editing business logic.

Applied: OrderService depends on a Repository interface; a factory/DI container injects the implementation. Tests inject an in-memory repo.

Python
class Repo(Protocol):
    def save(self, order): ...

class PostgresRepo:
    def save(self, order): print("pg save", order)

class OrderService:
    def __init__(self, repo: Repo):  # depends on abstraction
        self.repo = repo
    def place(self, order): self.repo.save(order)