Files
poimen-memory/knowledge/solid-dry-principles.md
T
rock a5ff20c9f7 feat: add 'mem learn' CLI for markdown knowledge ingestion
6 knowledge files: rust, SOLID/DRY, ast-grep, karpathy, golang, caveman
65 chunks ingested to log/knowledge/learn/latest.jsonl
Chunks on ## headings, SHA256 dedup, configurable chunk size
2026-08-29 22:04:14 -07:00

3.9 KiB

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?