{
  "slug": "software-engineering-patterns",
  "title": "Software Engineering Patterns",
  "domain_slug": "computer-science",
  "subdomain_slug": null,
  "difficulty": "intermediate",
  "tags": ["solid-principles", "design-patterns", "clean-architecture", "code-review", "testing"],
  "is_free": false,
  "token_count": 3440,
  "uncompressed_estimate": 3956,
  "savings_pct": 13.0,
  "rosetta": "[DECODE] cls=class|iface=interface|impl=implementation|fn=function|obj=object|dep=dependency|inj=injection|abs=abstract|conc=concrete|inst=instance|cfg=configuration|ctx=context|req=request|resp=response|repo=repository|svc=service|ctrl=controller|dto=data transfer object|SRP=Single Responsibility Principle|OCP=Open-Closed Principle|LSP=Liskov Substitution Principle|ISP=Interface Segregation Principle|DIP=Dependency Inversion Principle|DI=dependency injection|IoC=inversion of control",
  "content_compressed": "# Software Engineering Patterns Knowledge Pack\n\n## SOLID Principles\n\n### SRP — Single Responsibility Principle\nA cls should have only one reason to change. This means each cls encapsulates one cohesive responsibility. Violation indicators: cls names containing \"And\" or \"Manager\" that do many things, cls with methods operating on unrelated data, changes to one feature requiring edits to unrelated methods in the same cls.\n\nExample: A `UserService` that handles authentication, profile updates, AND email sending violates SRP. Refactor into `AuthService`, `ProfileService`, and `EmailService`. Each can change independently.\n\n### OCP — Open-Closed Principle\nSoftware entities should be open for extension but closed for modification. When new behavior is needed, extend through new code rather than modifying existing working code.\n\nImpl strategy: Use abs cls or iface to define contracts. New behavior is added as new conc cls that impl the iface. Strategy pattern is the canonical OCP impl. Example: Instead of a switch statement over payment types inside a `processPayment` fn, define a `PaymentProcessor` iface with a `process` method, then create `CreditCardProcessor`, `PayPalProcessor`, etc. Adding a new payment type means adding a new cls, not editing existing code.\n\n### LSP — Liskov Substitution Principle\nSubtypes must be substitutable for their base types without altering correctness. If cls B extends cls A, anywhere A is used, B should work without surprises.\n\nCommon violations: Square extending Rectangle (changing width changes height — breaks Rectangle behavior), throwing NotImplementedException in overridden methods, strengthening preconditions or weakening postconditions in subtype.\n\nTest: Can you use the derived cls everywhere the base cls is expected without checking the type? If you need `instanceof` checks, you're violating LSP.\n\n### ISP — Interface Segregation Principle\nClients should not be forced to depend on methods they don't use. Prefer many small, focused ifaces over one large \"fat\" iface.\n\nExample: A `Worker` iface with `work()`, `eat()`, `sleep()` forces a `Robot` cls to impl `eat()` and `sleep()`. Better: split into `Workable`, `Eatable`, `Sleepable` ifaces. `Human` impls all three, `Robot` impls only `Workable`.\n\n### DIP — Dependency Inversion Principle\nHigh-level modules should not depend on low-level modules. Both should depend on abs. Abs should not depend on details — details should depend on abs.\n\nPractical impl: A `NotificationService` (high-level) should depend on a `MessageSender` iface (abs), not directly on `SmtpEmailSender` (low-level detail). The conc impl is provided via DI. This allows swapping email for SMS or push notifications without changing the svc logic.\n\n## Design Patterns\n\n### Creational Patterns\n\n**Factory Method**: Define an iface for creating objs, but let subclasses decide which cls to inst. Use when: a cls can't anticipate which objs it needs to create, or you want subclasses to specify created objs.\n\n```\niface Logger { log(msg: string): void }\ncls FileLogger impls Logger { ... }\ncls ConsoleLogger impls Logger { ... }\ncls LoggerFactory { create(type: string): Logger }\n```\n\n**Abstract Factory**: Provides an iface for creating families of related objs without specifying conc cls. Use when: system should be independent of how products are created, or you need to ensure related objs are used together (e.g., UI themes: DarkButton + DarkTextBox, not DarkButton + LightTextBox).\n\n**Builder**: Separates construction of a complex obj from its representation. Use when: constructor has many parameters (especially optional ones), or obj requires step-by-step construction. Fluent API style: `new QueryBuilder().select('name').from('users').where('active = true').build()`\n\n**Singleton**: Ensures a cls has exactly one inst with a global access point. Use sparingly — often a sign of hidden global state. Acceptable uses: cfg managers, connection pools, loggers. Prefer DI container managing lifecycle over raw singleton.\n\n### Structural Patterns\n\n**Adapter**: Converts the iface of a cls into another iface clients expect. Use when integrating third-party libraries or legacy code with incompatible ifaces. Example: wrapping a legacy XML API to expose a JSON iface.\n\n**Decorator**: Attaches additional responsibilities to an obj dynamically. Alternative to subclassing for extending functionality. Example: `LoggingRepo` wraps `DatabaseRepo`, adds logging before/after each method call without modifying the original cls. Decorators are composable: `CachingRepo(LoggingRepo(DatabaseRepo()))`.\n\n**Facade**: Provides a simplified iface to a complex subsystem. Use when: you want to reduce coupling between clients and subsystem, or provide a simple entry point for common use cases while keeping the full subsystem accessible for advanced use.\n\n### Behavioral Patterns\n\n**Observer**: Defines a one-to-many dep between objs so that when one obj changes state, all dependents are notified. Foundation of event-driven architecture. Key components: Subject (maintains observer list, notifies on change), Observer (defines update iface). Modern impls: EventEmitter, RxJS Observables, pub/sub systems.\n\n**Strategy**: Defines a family of algorithms, encapsulates each one, makes them interchangeable. Eliminates conditional statements for selecting behavior. Example: sorting — pass a `Comparator` strategy rather than hardcoding sort logic. Compression: accept a `CompressionStrategy` iface with conc impls for gzip, brotli, zstd.\n\n**Command**: Encapsulates a req as an obj, allowing parameterization of clients with different reqs, queuing, logging, and undo operations. Components: Command (execute iface), ConcreteCommand (binds receiver + action), Invoker (triggers command), Receiver (performs actual work). Enables undo/redo stacks, macro recording, and transactional behavior.\n\n**Template Method**: Defines the skeleton of an algorithm in a base cls, letting subclasses override specific steps without changing the algorithm's structure. Example: a `DataMiner` base cls with final method `mine()` that calls `openFile()`, `extractData()`, `parseData()`, `analyzeData()`, `sendReport()`. Subclasses override `extractData()` and `parseData()` for CSV, JSON, XML formats.\n\n## Clean Architecture\n\n### Layer Structure (inside-out)\n1. **Entities** (innermost): Enterprise business rules. Pure domain objs with no deps on frameworks. Example: `Invoice` with `calculateTotal()`, `isOverdue()` — these rules exist regardless of delivery mechanism\n2. **Use Cases**: Application-specific business rules. Orchestrate data flow to/from entities. One cls per use case: `CreateOrderUseCase`, `CancelSubscriptionUseCase`. Depend on entity layer only\n3. **Interface Adapters**: Convert data between use case format and external format. Includes ctrl (web), presenters, repo impls, dto mappers. The repo pattern lives here — iface in use case layer, impl in adapter layer\n4. **Frameworks & Drivers** (outermost): Web framework, database, UI, external svcs. Most volatile layer — should be swappable without affecting inner layers\n\n### Dependency Rule\nDeps only point inward. Inner layers know nothing about outer layers. A use case defines a `UserRepo` iface; the adapter layer provides `PostgresUserRepo` impl. DI wires them together at composition root (application startup).\n\n### Practical Patterns\n- **Repo pattern**: abs data access behind an iface. Use case calls `repo.findById(id)` without knowing if it's SQL, NoSQL, API, or in-memory\n- **dto**: Carry data between layers without exposing domain entities to external concerns. Map at boundaries\n- **Composition root**: Single place where all deps are resolved and injected. Typically in main/startup. No service locator anti-pattern\n\n## Code Review Practices\n\n### What to Review\n1. **Correctness**: Does the code do what it claims? Edge cases handled? Off-by-one errors?\n2. **Design**: Does it follow established patterns? Are deps in the right direction? Is complexity warranted?\n3. **Readability**: Can a new team member understand this? Are names descriptive? Is the flow clear?\n4. **Testing**: Are tests meaningful (not just covering lines)? Do they test behavior, not impl? Are edge cases covered?\n5. **Security**: Input validation? SQL inj? XSS? Auth checks? Secrets exposed?\n6. **Perf**: O(n^2) where O(n) is possible? N+1 queries? Unbounded memory growth? Missing indexes?\n\n### Review Etiquette\n- Comment on the code, not the person. \"This fn could be clearer\" not \"You wrote confusing code\"\n- Distinguish: must-fix (blocking), should-fix (important), nit (style, optional)\n- Explain why, not just what. \"Consider extracting this because it violates SRP and will make testing harder\" vs \"Extract this\"\n- Approve when good enough — don't block on perfection. The perfect is the enemy of the shipped\n- Small PRs reviewed faster and more thoroughly. Target <400 lines changed\n\n## Testing Strategies\n\n### Test Pyramid\n- **Unit tests** (base, most numerous): Test individual fns/cls in isolation. Fast, deterministic, no I/O. Mock external deps at boundaries. Aim for high coverage of business logic\n- **Integration tests** (middle): Test interaction between components. Real database (use test containers), real file system, real HTTP between svcs. Slower but catch wiring issues\n- **E2E tests** (top, fewest): Test full user workflows through the entire stack. Slowest, most brittle. Cover critical paths only: signup, purchase, core features\n\n### Testing Principles\n- **Arrange-Act-Assert** (AAA): Setup test data, execute the fn under test, verify the result. Each test should have exactly one reason to fail\n- **Test behavior, not impl**: Test public iface, not private methods. Tests should survive refactoring if behavior is unchanged\n- **FIRST**: Fast, Independent (no test depends on another), Repeatable (deterministic), Self-validating (pass/fail, no manual inspection), Timely (written with or before code)\n- **Test doubles**: Stub (returns canned data), Mock (verifies interactions), Fake (working impl with shortcuts, like in-memory db), Spy (records calls for later assertion)\n- **Mutation testing**: Modify code (change `>` to `>=`, delete lines) and verify tests catch it. Reveals weak tests that pass regardless of correctness"
}