Files
poimen-memory/knowledge/solid-dry-principles.md
T

70 lines
3.9 KiB
Markdown
Raw Normal View History

# SOLID & DRY Design Principles
## Single Responsibility Principle (SRP)
- A class/module should have one and only one reason to change.
- Each module owns exactly one actor's requirements.
- Bad: `UserService` that handles auth, email, and database. Good: separate `AuthService`, `EmailService`, `UserRepository`.
- In Rust: one struct per concern. `ChunkProcessor` doesn't also handle HTTP routing.
## Open/Closed Principle (OCP)
- Software entities should be open for extension, closed for modification.
- Use traits/interfaces to allow new behavior without changing existing code.
- Strategy pattern: `trait Scorer { fn score(&self, doc: &Doc) -> f64; }` — add new scorers without modifying search.
- In Rust: trait objects or generics. `fn process<S: Strategy>(s: &S)` — new strategies don't touch `process`.
## Liskov Substitution Principle (LSP)
- Subtypes must be substitutable for their base types without breaking correctness.
- If `fn accept(animal: &dyn Animal)` works with `Dog`, it must work with `Cat` too.
- Violated when: subtype throws unexpected errors, ignores base contract, strengthens preconditions.
- In Rust: trait implementations must honor the trait's documented contract.
## Interface Segregation Principle (ISP)
- Clients should not be forced to depend on interfaces they don't use.
- Many small traits > one fat trait.
- Bad: `trait Repository { fn read(); fn write(); fn delete(); fn audit(); }` — read-only clients forced to see write methods.
- Good: `trait Readable`, `trait Writable`, `trait Auditable` — compose as needed.
- In Rust: supertraits for composition: `trait FullRepo: Readable + Writable + Auditable {}`
## Dependency Inversion Principle (DIP)
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- In Rust: accept `impl Trait` or `&dyn Trait`, not concrete types.
- `fn search(store: &dyn VectorStore)` — works with Postgres, OpenSearch, or in-memory mock.
- Constructor injection: `struct SearchEngine { store: Box<dyn VectorStore> }`
## DRY (Don't Repeat Yourself)
- Every piece of knowledge should have a single, unambiguous, authoritative representation.
- DRY is about knowledge, not code. Two functions with same code but different reasons to change are NOT duplication.
- Extract when: same logic appears 3+ times AND changes for the same reason.
- Wrong DRY: coupling unrelated code just because it looks similar. Right DRY: shared business rules in one place.
## WET (Write Everything Twice) — When DRY Goes Wrong
- Premature DRY creates coupling worse than duplication.
- Rule of three: duplicate is fine, triplicate means extract.
- Tests should be WET — readability > DRYness in test code.
- Configuration can be WET — explicit is better than magic shared config.
## KISS (Keep It Simple, Stupid)
- Simplest solution that works is usually the best.
- Avoid: premature abstraction, speculative generality, framework-itis.
- Measure complexity: if a new team member can't understand it in 15 minutes, simplify.
## YAGNI (You Aren't Gonna Need It)
- Don't build features until you actually need them.
- Speculative code rots — it's untested, unmaintained, and misleading.
- Exception: known architectural boundaries (API versioning, database migrations).
## Composition Over Inheritance
- Prefer composing objects over class hierarchies.
- In Rust: no inheritance. Composition is the default via struct fields + trait delegation.
- `struct HttpServer { router: Router, auth: AuthMiddleware, rate_limiter: RateLimiter }`
## Law of Demeter
- Only talk to your immediate friends. Don't chain: `a.b().c().d()`.
- Tell, don't ask: `order.ship()` not `order.get_warehouse().get_shipping().create_label()`.
- In Rust: expose methods that encapsulate internal structure.
## Practical Application
- Start concrete, extract abstractions when patterns emerge.
- Refactor in small steps with tests as safety net.
- Code review checklist: SRP violated? Unnecessary coupling? Duplicated knowledge? Over-engineered?