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
3.9 KiB
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:
UserServicethat handles auth, email, and database. Good: separateAuthService,EmailService,UserRepository. - In Rust: one struct per concern.
ChunkProcessordoesn'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 touchprocess.
Liskov Substitution Principle (LSP)
- Subtypes must be substitutable for their base types without breaking correctness.
- If
fn accept(animal: &dyn Animal)works withDog, it must work withCattoo. - 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 Traitor&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()notorder.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?