# SOLID Walkthrough — Learn by Doing

## Before We Begin

<!-- hint:slides topic="The five SOLID principles: SRP, OCP, LSP, ISP, DIP — what each means, when to apply, and real-world examples" slides="6" -->

SOLID is an acronym for five principles that help design maintainable, flexible systems: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion. They're guides, not laws—understanding when to apply them matters more than rigid adherence.

**Diagnostic question:** Think of a class or module you've had to change recently. Did you modify it to add one thing or many? What would have made that change easier or safer?

**Checkpoint:** You can name at least one SOLID principle you've heard of and one situation where design made changes harder.

---

## Step 1: Identifying Responsibility Violations

<!-- hint:diagram mermaid-type="flowchart" topic="Single Responsibility Principle" -->
<!-- hint:card type="concept" title="SRP: One reason to change" -->

**Task:** Find a class or module in your codebase that does more than one kind of thing (e.g., fetches data, formats it, and sends emails). List each distinct responsibility.

**Question:** What would happen if the email provider API changed? Would you have to touch code that fetches data? What does that tell you about responsibility boundaries?

**Checkpoint:** The user can identify multiple responsibilities and explain why separating them helps.

---

## Step 2: Extracting by SRP

<!-- hint:code language="javascript" highlight="1,5" -->

**Task:** Take the module from Step 1 (or a simplified version). Extract one responsibility into a separate class or module. Ensure the original delegates to it.

**Question:** How did you decide the boundary? Could you have split differently? What would "too granular" look like?

**Checkpoint:** The user has at least one extracted component with a single, clear responsibility.

---

## Step 3: Open/Closed in Practice

<!-- hint:card type="concept" title="Open/Closed: Open for extension, closed for modification" -->

**Task:** Design a small discount calculator. Instead of a big if/else for each discount type, use a strategy-like approach: each discount type is a separate implementation that can be added without editing existing code.

**Question:** What would you need to change to add a "buy 2 get 1 free" discount? Would you edit old code or add new code?

**Checkpoint:** The user has a design where new behavior is added by extension, not modification.

---

## Step 4: Interface Segregation

<!-- hint:card type="concept" title="ISP: Clients shouldn't depend on methods they don't use" -->

**Task:** Imagine an interface with 5 methods. Two different clients each need only 2 of those methods. Split the interface so each client depends only on what it uses.

**Question:** What problems arise when a client is forced to depend on methods it never calls?

**Checkpoint:** The user understands ISP and has created smaller, focused interfaces.

---

## Step 5: Dependency Inversion

<!-- hint:card type="concept" title="DIP: Depend on abstractions, not concretions" -->

**Task:** Take a class that directly instantiates a dependency (e.g., `new HttpClient()`). Refactor so the dependency is injected via the constructor. The class should depend on an interface, not a concrete implementation.

**Question:** How does injection make testing easier? What would you pass in a unit test?

**Checkpoint:** The user can apply DIP with constructor injection and explain testability benefits.

---

## Step 6: Applying Multiple Principles

**Task:** Review a small feature you've built. Apply at least 2 SOLID principles: perhaps SRP (split a mixed class) and DIP (inject a dependency). Describe the before/after.

**Question:** When might strict SOLID adherence be overkill? How do you balance principles with pragmatism?

**Checkpoint:** The user can articulate tradeoffs and apply principles selectively.

<!-- hint:celebrate -->
