Strategic architecture, tactical design, and testable code principles (SOLID, Clean Architecture, Design Patterns, Testable Design)
97
97%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Structural design patterns for solving recurring design problems with proven solutions.
Output: Concrete problem statement without pattern jargon.
Ask:
Example:
Problem: Payment processing logic uses if/else chains to select processors.
Adding new processors requires editing existing code (OCP violation).Output: Pattern choice with explicit win condition.
Ask before choosing a pattern:
Example:
Pattern: Strategy (not Factory).
Win condition: Eliminates if/else chains for payment processor selection.
Cost: One interface + implementations. Benefit: Easy to add new processors.Output: Pattern applied with before/after comparison.
Template:
Pattern: Strategy
Problem: if/else chain for payment processor selection
Solution: PaymentProcessor interface + implementations (Stripe, PayPal, etc.)
Validation: New processors added without editing existing codeUse when: Conditional logic selects between algorithms.
Example:
// Before: if/else chain
if (type === 'stripe') {
// Stripe logic
} else if (type === 'paypal') {
// PayPal logic
}
// After: Strategy pattern
interface PaymentProcessor {
process(amount: number): Promise<Receipt>
}
class StripeProcessor implements PaymentProcessor { /* ... */ }
class PayPalProcessor implements PaymentProcessor { /* ... */ }
// Select strategy
const processor = processors[type]
await processor.process(amount)Use when: Object creation logic is complex or conditional.
Example:
// Before: constructor with complex logic
class Report {
constructor(type: string) {
if (type === 'pdf') {
// PDF setup
} else if (type === 'csv') {
// CSV setup
}
}
}
// After: Factory pattern
interface Report { generate(): string }
class ReportFactory {
create(type: string): Report {
switch (type) {
case 'pdf': return new PdfReport()
case 'csv': return new CsvReport()
default: throw new Error('Unknown type')
}
}
}Use when: Integrating external APIs or legacy systems with incompatible interfaces.
Example:
// External API (incompatible interface)
class LegacyEmailService {
sendMail(to: string, subject: string, body: string): void { /* ... */ }
}
// Domain interface
interface IEmailService {
send(email: Email): Promise<void>
}
// Adapter
class EmailServiceAdapter implements IEmailService {
constructor(private legacy: LegacyEmailService) {}
async send(email: Email): Promise<void> {
this.legacy.sendMail(email.to, email.subject, email.body)
}
}Use when: Multiple components need to react to state changes.
Example:
interface Observer {
update(event: Event): void
}
class Subject {
private observers: Observer[] = []
attach(observer: Observer): void {
this.observers.push(observer)
}
notify(event: Event): void {
this.observers.forEach(o => o.update(event))
}
}Use when: Integrating with external systems that should not pollute your domain.
Example:
// External API with poor design
interface ExternalUserAPI {
getUserData(id: number): { usr_nm: string, eml: string }
}
// Anti-Corruption Layer
class UserGateway {
constructor(private api: ExternalUserAPI) {}
async getUser(id: string): Promise<User> {
const data = this.api.getUserData(Number(id))
return new User({ name: data.usr_nm, email: data.eml })
}
}Use when: Separating testable logic from hard-to-test infrastructure.
Example:
// Humble object (hard to test, minimal logic)
class HttpController {
constructor(private useCase: CreateOrderUseCase) {}
async handle(req: Request, res: Response): Promise<void> {
const input = this.mapRequest(req)
const output = await this.useCase.execute(input)
res.json(output)
}
}
// Testable logic (easy to test, no infrastructure)
class CreateOrderUseCase {
async execute(input: CreateOrderInput): Promise<CreateOrderOutput> {
// Business logic here
}
}BAD: Add Factory pattern "in case" we need multiple implementations.
GOOD: Wait for concrete need (second implementation) before extracting pattern.
BAD: Wrap simple logic in Strategy/Factory/Builder for "future flexibility."
GOOD: Start simple; refactor to pattern when complexity justifies it.
BAD: Copy design patterns because they "look professional."
GOOD: Apply patterns to solve concrete problems in your codebase.
BAD: Add cache or pool because function "might be slow."
GOOD: Measure baseline, optimize when threshold is exceeded.
# Find Strategy/Factory candidates (conditional logic)
rg -n "if.*type.*==|switch.*\(type\)|instanceof" src# Find Adapter candidates (external API usage)
rg -n "import.*from.*external|import.*sdk" src# Find Observer candidates (event handling)
rg -n "addEventListener|on\(|emit\(|dispatch\(" srcCreational: Control object creation (Factory, Builder, Singleton)
Structural: Compose objects and classes (Adapter, Decorator, Facade)
Behavioral: Manage algorithms and responsibilities (Strategy, Observer, Command)
clean-architecture
evals
references
design-patterns
solid-principles
testable-design