sdd-mcp-server 2.1.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -38
- package/atomicWrite.js +86 -0
- package/dist/adapters/cli/SDDToolAdapter.js +12 -2
- package/dist/adapters/cli/SDDToolAdapter.js.map +1 -1
- package/dist/cli/install-skills.d.ts +38 -2
- package/dist/cli/install-skills.js +226 -10
- package/dist/cli/install-skills.js.map +1 -1
- package/dist/cli/migrate-kiro.js +4 -4
- package/dist/cli/sdd-mcp-cli.d.ts +2 -1
- package/dist/cli/sdd-mcp-cli.js +21 -11
- package/dist/cli/sdd-mcp-cli.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/utils/atomicWrite.d.ts +55 -0
- package/dist/utils/atomicWrite.js +84 -0
- package/dist/utils/atomicWrite.js.map +1 -0
- package/mcp-server.js +5 -4
- package/package.json +5 -3
- package/sdd-entry.js +31 -0
- package/skills/sdd-commit/SKILL.md +14 -0
- package/skills/sdd-design/SKILL.md +15 -0
- package/skills/sdd-implement/SKILL.md +15 -0
- package/skills/sdd-requirements/SKILL.md +9 -2
- package/skills/sdd-tasks/SKILL.md +14 -0
- package/steering/AGENTS.md +281 -0
- package/steering/commit.md +59 -0
- package/steering/linus-review.md +153 -0
- package/steering/owasp-top10-check.md +49 -0
- package/steering/principles.md +639 -0
- package/steering/tdd-guideline.md +324 -0
- package/dist/utils/sddPaths.d.ts +0 -69
- package/dist/utils/sddPaths.js +0 -138
- package/dist/utils/sddPaths.js.map +0 -1
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
# Core Coding Principles and Patterns
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document defines the fundamental software engineering principles that MUST be followed in all code development. These principles ensure code quality, maintainability, scalability, and team collaboration.
|
|
6
|
+
|
|
7
|
+
**Golden Rule**: Always ask "Does this code follow SOLID, DRY, KISS, YAGNI, Separation of Concerns, and Modularity?" before committing.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## SOLID Principles (Object-Oriented Design)
|
|
12
|
+
|
|
13
|
+
### S - Single Responsibility Principle (SRP)
|
|
14
|
+
|
|
15
|
+
**Definition**: A class/module should have one, and only one, reason to change. Each component should do one thing well.
|
|
16
|
+
|
|
17
|
+
**✅ Good Example**:
|
|
18
|
+
```typescript
|
|
19
|
+
// Good: Separate responsibilities
|
|
20
|
+
class UserRepository {
|
|
21
|
+
save(user: User): Promise<void> { /* DB logic */ }
|
|
22
|
+
findById(id: string): Promise<User> { /* DB logic */ }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class UserValidator {
|
|
26
|
+
validate(user: User): ValidationResult { /* Validation logic */ }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class EmailService {
|
|
30
|
+
sendWelcomeEmail(user: User): Promise<void> { /* Email logic */ }
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**❌ Bad Example**:
|
|
35
|
+
```typescript
|
|
36
|
+
// Bad: Multiple responsibilities in one class
|
|
37
|
+
class UserManager {
|
|
38
|
+
save(user: User) { /* DB logic */ }
|
|
39
|
+
validate(user: User) { /* Validation */ }
|
|
40
|
+
sendEmail(user: User) { /* Email */ }
|
|
41
|
+
generateReport(user: User) { /* Reporting */ }
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Application**:
|
|
46
|
+
- Functions should do ONE thing
|
|
47
|
+
- Classes should have ONE reason to change
|
|
48
|
+
- Modules should have ONE clear purpose
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### O - Open/Closed Principle (OCP)
|
|
53
|
+
|
|
54
|
+
**Definition**: Software entities should be open for extension, but closed for modification.
|
|
55
|
+
|
|
56
|
+
**✅ Good Example**:
|
|
57
|
+
```typescript
|
|
58
|
+
// Good: Use interfaces/abstractions for extension
|
|
59
|
+
interface PaymentProcessor {
|
|
60
|
+
process(amount: number): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class CreditCardProcessor implements PaymentProcessor {
|
|
64
|
+
process(amount: number) { /* Credit card logic */ }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
class PayPalProcessor implements PaymentProcessor {
|
|
68
|
+
process(amount: number) { /* PayPal logic */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Adding new payment method doesn't modify existing code
|
|
72
|
+
class CryptoProcessor implements PaymentProcessor {
|
|
73
|
+
process(amount: number) { /* Crypto logic */ }
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**❌ Bad Example**:
|
|
78
|
+
```typescript
|
|
79
|
+
// Bad: Modifying existing code to add features
|
|
80
|
+
class PaymentProcessor {
|
|
81
|
+
process(amount: number, type: 'card' | 'paypal' | 'crypto') {
|
|
82
|
+
if (type === 'card') { /* Card logic */ }
|
|
83
|
+
else if (type === 'paypal') { /* PayPal logic */ }
|
|
84
|
+
else if (type === 'crypto') { /* Crypto logic */ } // Modified existing code
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Application**:
|
|
90
|
+
- Use interfaces/abstract classes
|
|
91
|
+
- Favor composition over inheritance
|
|
92
|
+
- Use dependency injection
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### L - Liskov Substitution Principle (LSP)
|
|
97
|
+
|
|
98
|
+
**Definition**: Derived classes must be substitutable for their base classes without altering correctness.
|
|
99
|
+
|
|
100
|
+
**✅ Good Example**:
|
|
101
|
+
```typescript
|
|
102
|
+
// Good: Subclass maintains expected behavior
|
|
103
|
+
class Rectangle {
|
|
104
|
+
constructor(protected width: number, protected height: number) {}
|
|
105
|
+
|
|
106
|
+
getArea(): number {
|
|
107
|
+
return this.width * this.height;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class Square extends Rectangle {
|
|
112
|
+
constructor(size: number) {
|
|
113
|
+
super(size, size);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
getArea(): number {
|
|
117
|
+
return super.getArea(); // Maintains expected behavior
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**❌ Bad Example**:
|
|
123
|
+
```typescript
|
|
124
|
+
// Bad: Subclass violates parent contract
|
|
125
|
+
class Rectangle {
|
|
126
|
+
setWidth(w: number) { this.width = w; }
|
|
127
|
+
setHeight(h: number) { this.height = h; }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
class Square extends Rectangle {
|
|
131
|
+
setWidth(w: number) {
|
|
132
|
+
this.width = w;
|
|
133
|
+
this.height = w; // Violates rectangle behavior!
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Application**:
|
|
139
|
+
- Subclasses should honor parent contracts
|
|
140
|
+
- Don't surprise callers with unexpected behavior
|
|
141
|
+
- Use composition when inheritance doesn't fit
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### I - Interface Segregation Principle (ISP)
|
|
146
|
+
|
|
147
|
+
**Definition**: No client should be forced to depend on methods it doesn't use.
|
|
148
|
+
|
|
149
|
+
**✅ Good Example**:
|
|
150
|
+
```typescript
|
|
151
|
+
// Good: Small, focused interfaces
|
|
152
|
+
interface Readable {
|
|
153
|
+
read(): string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface Writable {
|
|
157
|
+
write(data: string): void;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
class File implements Readable, Writable {
|
|
161
|
+
read() { return "data"; }
|
|
162
|
+
write(data: string) { /* write */ }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
class ReadOnlyFile implements Readable {
|
|
166
|
+
read() { return "data"; }
|
|
167
|
+
// Not forced to implement write()
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**❌ Bad Example**:
|
|
172
|
+
```typescript
|
|
173
|
+
// Bad: Bloated interface
|
|
174
|
+
interface FileOperations {
|
|
175
|
+
read(): string;
|
|
176
|
+
write(data: string): void;
|
|
177
|
+
delete(): void;
|
|
178
|
+
compress(): void;
|
|
179
|
+
encrypt(): void;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
class ReadOnlyFile implements FileOperations {
|
|
183
|
+
read() { return "data"; }
|
|
184
|
+
write() { throw new Error("Not supported"); } // Forced to implement!
|
|
185
|
+
delete() { throw new Error("Not supported"); }
|
|
186
|
+
compress() { throw new Error("Not supported"); }
|
|
187
|
+
encrypt() { throw new Error("Not supported"); }
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Application**:
|
|
192
|
+
- Keep interfaces small and focused
|
|
193
|
+
- Split large interfaces into smaller ones
|
|
194
|
+
- Use role interfaces
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### D - Dependency Inversion Principle (DIP)
|
|
199
|
+
|
|
200
|
+
**Definition**: High-level modules should not depend on low-level modules. Both should depend on abstractions.
|
|
201
|
+
|
|
202
|
+
**✅ Good Example**:
|
|
203
|
+
```typescript
|
|
204
|
+
// Good: Depend on abstractions
|
|
205
|
+
interface Logger {
|
|
206
|
+
log(message: string): void;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
class UserService {
|
|
210
|
+
constructor(private logger: Logger) {} // Depends on abstraction
|
|
211
|
+
|
|
212
|
+
createUser(user: User) {
|
|
213
|
+
this.logger.log(`Creating user: ${user.name}`);
|
|
214
|
+
// ...
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Can inject any logger implementation
|
|
219
|
+
class ConsoleLogger implements Logger {
|
|
220
|
+
log(message: string) { console.log(message); }
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
class FileLogger implements Logger {
|
|
224
|
+
log(message: string) { /* write to file */ }
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**❌ Bad Example**:
|
|
229
|
+
```typescript
|
|
230
|
+
// Bad: Direct dependency on concrete class
|
|
231
|
+
class UserService {
|
|
232
|
+
private logger = new ConsoleLogger(); // Tightly coupled!
|
|
233
|
+
|
|
234
|
+
createUser(user: User) {
|
|
235
|
+
this.logger.log(`Creating user: ${user.name}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Application**:
|
|
241
|
+
- Use dependency injection
|
|
242
|
+
- Program to interfaces, not implementations
|
|
243
|
+
- Invert control flow
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## DRY (Don't Repeat Yourself)
|
|
248
|
+
|
|
249
|
+
**Definition**: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
|
|
250
|
+
|
|
251
|
+
**✅ Good Example**:
|
|
252
|
+
```typescript
|
|
253
|
+
// Good: Extract common logic
|
|
254
|
+
function calculateDiscount(price: number, discountPercent: number): number {
|
|
255
|
+
return price * (1 - discountPercent / 100);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const regularPrice = calculateDiscount(100, 10);
|
|
259
|
+
const premiumPrice = calculateDiscount(200, 15);
|
|
260
|
+
const vipPrice = calculateDiscount(300, 20);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**❌ Bad Example**:
|
|
264
|
+
```typescript
|
|
265
|
+
// Bad: Repeated logic
|
|
266
|
+
const regularPrice = 100 * (1 - 10 / 100);
|
|
267
|
+
const premiumPrice = 200 * (1 - 15 / 100);
|
|
268
|
+
const vipPrice = 300 * (1 - 20 / 100);
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**When to Break DRY**:
|
|
272
|
+
- **Accidental Duplication**: Similar code, different concerns
|
|
273
|
+
- **Premature Abstraction**: Wait until you have 3+ instances
|
|
274
|
+
- **Different Change Reasons**: Code that looks similar but changes for different business reasons
|
|
275
|
+
|
|
276
|
+
**Application**:
|
|
277
|
+
- Extract common functions/classes
|
|
278
|
+
- Use inheritance/composition for shared behavior
|
|
279
|
+
- Apply at data level (single source of truth)
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## KISS (Keep It Simple, Stupid)
|
|
284
|
+
|
|
285
|
+
**Definition**: Simplicity should be a key goal in design. Avoid unnecessary complexity.
|
|
286
|
+
|
|
287
|
+
**✅ Good Example**:
|
|
288
|
+
```typescript
|
|
289
|
+
// Good: Simple and clear
|
|
290
|
+
function isEven(num: number): boolean {
|
|
291
|
+
return num % 2 === 0;
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**❌ Bad Example**:
|
|
296
|
+
```typescript
|
|
297
|
+
// Bad: Overly complex
|
|
298
|
+
function isEven(num: number): boolean {
|
|
299
|
+
return ((num / 2) === Math.floor(num / 2)) &&
|
|
300
|
+
(parseInt(num.toString()) % 2 === 0) &&
|
|
301
|
+
((num & 1) === 0);
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Guidelines**:
|
|
306
|
+
- Write code that's easy to understand
|
|
307
|
+
- Avoid clever tricks that sacrifice readability
|
|
308
|
+
- Break complex functions into smaller, simple ones
|
|
309
|
+
- Use clear naming over comments
|
|
310
|
+
|
|
311
|
+
**Application**:
|
|
312
|
+
- Prefer straightforward solutions
|
|
313
|
+
- Avoid premature optimization
|
|
314
|
+
- Refactor complex code into simple pieces
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## YAGNI (You Aren't Gonna Need It)
|
|
319
|
+
|
|
320
|
+
**Definition**: Don't implement functionality until it's actually needed. Avoid speculative features.
|
|
321
|
+
|
|
322
|
+
**✅ Good Example**:
|
|
323
|
+
```typescript
|
|
324
|
+
// Good: Implement only what's needed now
|
|
325
|
+
class User {
|
|
326
|
+
constructor(
|
|
327
|
+
public id: string,
|
|
328
|
+
public email: string,
|
|
329
|
+
public name: string
|
|
330
|
+
) {}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**❌ Bad Example**:
|
|
335
|
+
```typescript
|
|
336
|
+
// Bad: Anticipating future needs
|
|
337
|
+
class User {
|
|
338
|
+
constructor(
|
|
339
|
+
public id: string,
|
|
340
|
+
public email: string,
|
|
341
|
+
public name: string,
|
|
342
|
+
public secondaryEmail?: string, // Not needed yet
|
|
343
|
+
public phoneNumbers?: string[], // Not needed yet
|
|
344
|
+
public socialProfiles?: object[], // Not needed yet
|
|
345
|
+
public preferences?: UserPrefs, // Not needed yet
|
|
346
|
+
public metadata?: Record<string, any> // Not needed yet
|
|
347
|
+
) {}
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Guidelines**:
|
|
352
|
+
- Implement features only when required
|
|
353
|
+
- Don't add "might need" functionality
|
|
354
|
+
- Refactor when new requirements come
|
|
355
|
+
- Focus on current user stories
|
|
356
|
+
|
|
357
|
+
**Application**:
|
|
358
|
+
- Stick to requirements
|
|
359
|
+
- Avoid gold-plating
|
|
360
|
+
- Iterate based on real needs
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Separation of Concerns (SoC)
|
|
365
|
+
|
|
366
|
+
**Definition**: Separate a system into distinct sections, each addressing a separate concern.
|
|
367
|
+
|
|
368
|
+
**✅ Good Example**:
|
|
369
|
+
```typescript
|
|
370
|
+
// Good: Layered architecture
|
|
371
|
+
// Presentation Layer
|
|
372
|
+
class UserController {
|
|
373
|
+
createUser(req, res) {
|
|
374
|
+
const user = this.userService.create(req.body);
|
|
375
|
+
res.json(user);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Business Logic Layer
|
|
380
|
+
class UserService {
|
|
381
|
+
create(data: UserData): User {
|
|
382
|
+
this.validate(data);
|
|
383
|
+
return this.repository.save(data);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Data Access Layer
|
|
388
|
+
class UserRepository {
|
|
389
|
+
save(data: UserData): User {
|
|
390
|
+
return db.users.insert(data);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**❌ Bad Example**:
|
|
396
|
+
```typescript
|
|
397
|
+
// Bad: Mixed concerns
|
|
398
|
+
class UserController {
|
|
399
|
+
createUser(req, res) {
|
|
400
|
+
// Validation (business logic)
|
|
401
|
+
if (!req.body.email) throw new Error('Email required');
|
|
402
|
+
|
|
403
|
+
// Database access (data layer)
|
|
404
|
+
const user = db.users.insert(req.body);
|
|
405
|
+
|
|
406
|
+
// Email sending (external service)
|
|
407
|
+
emailService.send(user.email, 'Welcome!');
|
|
408
|
+
|
|
409
|
+
// Response (presentation)
|
|
410
|
+
res.json(user);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**Common Separations**:
|
|
416
|
+
- **Presentation** (UI, API) ↔ **Business Logic** ↔ **Data Access**
|
|
417
|
+
- **Domain Model** ↔ **Infrastructure**
|
|
418
|
+
- **Configuration** ↔ **Implementation**
|
|
419
|
+
|
|
420
|
+
**Application**:
|
|
421
|
+
- Use layered architecture
|
|
422
|
+
- Keep business logic independent of frameworks
|
|
423
|
+
- Separate I/O from computation
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Modularity
|
|
428
|
+
|
|
429
|
+
**Definition**: Design systems as a collection of independent, interchangeable modules with well-defined interfaces.
|
|
430
|
+
|
|
431
|
+
**Principles**:
|
|
432
|
+
- **High Cohesion**: Elements within a module are strongly related
|
|
433
|
+
- **Low Coupling**: Modules have minimal dependencies on each other
|
|
434
|
+
- **Encapsulation**: Hide internal details, expose only necessary interfaces
|
|
435
|
+
|
|
436
|
+
**✅ Good Example**:
|
|
437
|
+
```typescript
|
|
438
|
+
// Good: Modular design
|
|
439
|
+
// auth/index.ts (Authentication Module)
|
|
440
|
+
export interface AuthService {
|
|
441
|
+
login(email: string, password: string): Promise<Token>;
|
|
442
|
+
logout(token: string): Promise<void>;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export class JWTAuthService implements AuthService {
|
|
446
|
+
login(email, password) { /* JWT logic */ }
|
|
447
|
+
logout(token) { /* JWT logout */ }
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// payment/index.ts (Payment Module)
|
|
451
|
+
export interface PaymentService {
|
|
452
|
+
charge(amount: number): Promise<Receipt>;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export class StripePaymentService implements PaymentService {
|
|
456
|
+
charge(amount) { /* Stripe logic */ }
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Each module is independent, replaceable, testable
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**❌ Bad Example**:
|
|
463
|
+
```typescript
|
|
464
|
+
// Bad: Tightly coupled, non-modular
|
|
465
|
+
class Application {
|
|
466
|
+
doEverything() {
|
|
467
|
+
// Auth logic mixed in
|
|
468
|
+
const user = db.users.find(email);
|
|
469
|
+
if (bcrypt.compare(password, user.hash)) {
|
|
470
|
+
// Payment logic mixed in
|
|
471
|
+
const charge = stripe.charges.create({amount: 100});
|
|
472
|
+
// Email logic mixed in
|
|
473
|
+
sendgrid.send({to: user.email, body: 'Thanks!'});
|
|
474
|
+
// Logging mixed in
|
|
475
|
+
winston.log('Charged user');
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Module Design Guidelines**:
|
|
482
|
+
- One module = one responsibility
|
|
483
|
+
- Minimal cross-module dependencies
|
|
484
|
+
- Clear public interfaces
|
|
485
|
+
- Hide implementation details
|
|
486
|
+
|
|
487
|
+
**Application**:
|
|
488
|
+
- Organize code by feature/domain, not by type
|
|
489
|
+
- Use dependency injection for inter-module communication
|
|
490
|
+
- Create facade interfaces for complex modules
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Integration of Principles
|
|
495
|
+
|
|
496
|
+
### How Principles Work Together
|
|
497
|
+
|
|
498
|
+
**Example: User Registration Feature**
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
// SOLID + SoC + Modularity
|
|
502
|
+
interface UserRepository { // DIP: Depend on abstraction
|
|
503
|
+
save(user: User): Promise<User>; // ISP: Focused interface
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
interface EmailService { // ISP: Separate interface
|
|
507
|
+
send(to: string, subject: string, body: string): Promise<void>;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
class UserRegistrationService { // SRP: Single responsibility
|
|
511
|
+
constructor(
|
|
512
|
+
private userRepo: UserRepository, // DIP: Inject dependencies
|
|
513
|
+
private emailService: EmailService
|
|
514
|
+
) {}
|
|
515
|
+
|
|
516
|
+
async register(data: UserData): Promise<User> { // KISS: Simple method
|
|
517
|
+
// YAGNI: Only what's needed for registration
|
|
518
|
+
const user = this.validate(data); // SoC: Separate validation
|
|
519
|
+
const saved = await this.userRepo.save(user);
|
|
520
|
+
await this.emailService.send(saved.email, 'Welcome', 'Thanks for registering!');
|
|
521
|
+
return saved;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private validate(data: UserData): User { // DRY: Reusable validation
|
|
525
|
+
if (!data.email || !data.password) {
|
|
526
|
+
throw new ValidationError('Email and password required');
|
|
527
|
+
}
|
|
528
|
+
return new User(data);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
**Principles Applied**:
|
|
534
|
+
- ✅ **SRP**: UserRegistrationService only handles registration
|
|
535
|
+
- ✅ **OCP**: Can extend with new validators without modifying
|
|
536
|
+
- ✅ **DIP**: Depends on interfaces, not concrete implementations
|
|
537
|
+
- ✅ **ISP**: Small, focused interfaces
|
|
538
|
+
- ✅ **SoC**: Validation, persistence, and email are separated
|
|
539
|
+
- ✅ **Modularity**: Can swap UserRepository or EmailService implementations
|
|
540
|
+
- ✅ **KISS**: Clear, simple logic
|
|
541
|
+
- ✅ **YAGNI**: No speculative features
|
|
542
|
+
- ✅ **DRY**: Validation extracted to private method
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## Code Review Checklist
|
|
547
|
+
|
|
548
|
+
Use this checklist when reviewing code:
|
|
549
|
+
|
|
550
|
+
### SOLID
|
|
551
|
+
- [ ] Does each class/module have a single responsibility?
|
|
552
|
+
- [ ] Can I extend functionality without modifying existing code?
|
|
553
|
+
- [ ] Are abstractions used instead of concrete implementations?
|
|
554
|
+
- [ ] Are interfaces small and focused?
|
|
555
|
+
- [ ] Do dependencies point toward abstractions?
|
|
556
|
+
|
|
557
|
+
### DRY
|
|
558
|
+
- [ ] Is there any duplicated logic that should be extracted?
|
|
559
|
+
- [ ] Is duplication intentional (different concerns)?
|
|
560
|
+
- [ ] Are magic numbers/strings extracted to constants?
|
|
561
|
+
|
|
562
|
+
### KISS
|
|
563
|
+
- [ ] Is the solution as simple as possible?
|
|
564
|
+
- [ ] Can complex logic be broken into simpler pieces?
|
|
565
|
+
- [ ] Are variable/function names clear without comments?
|
|
566
|
+
|
|
567
|
+
### YAGNI
|
|
568
|
+
- [ ] Is every feature actually needed now?
|
|
569
|
+
- [ ] Are there "future-proofing" additions that can be removed?
|
|
570
|
+
- [ ] Does the code solve current requirements without speculation?
|
|
571
|
+
|
|
572
|
+
### Separation of Concerns
|
|
573
|
+
- [ ] Are different concerns (UI, business logic, data) separated?
|
|
574
|
+
- [ ] Is business logic independent of frameworks?
|
|
575
|
+
- [ ] Are responsibilities clearly divided?
|
|
576
|
+
|
|
577
|
+
### Modularity
|
|
578
|
+
- [ ] Are modules cohesive (related functionality together)?
|
|
579
|
+
- [ ] Are modules loosely coupled (minimal dependencies)?
|
|
580
|
+
- [ ] Are implementation details hidden?
|
|
581
|
+
- [ ] Are public interfaces clear and minimal?
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## Common Anti-Patterns to Avoid
|
|
586
|
+
|
|
587
|
+
| Anti-Pattern | Violates | Fix |
|
|
588
|
+
|--------------|----------|-----|
|
|
589
|
+
| God Object/Class | SRP, SoC | Split into focused classes |
|
|
590
|
+
| Copy-Paste Programming | DRY | Extract common logic |
|
|
591
|
+
| Premature Optimization | KISS, YAGNI | Optimize when needed |
|
|
592
|
+
| Big Ball of Mud | All principles | Refactor with clear architecture |
|
|
593
|
+
| Shotgun Surgery | OCP, Modularity | Centralize related changes |
|
|
594
|
+
| Feature Envy | SRP, SoC | Move behavior to appropriate class |
|
|
595
|
+
| Long Parameter List | KISS, ISP | Use parameter objects |
|
|
596
|
+
| Magic Numbers | DRY | Extract to named constants |
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
## Language-Specific Guidance
|
|
601
|
+
|
|
602
|
+
### TypeScript/JavaScript
|
|
603
|
+
- Use interfaces for contracts (ISP, DIP)
|
|
604
|
+
- Favor composition with functions (OCP)
|
|
605
|
+
- Use modules for separation (SoC, Modularity)
|
|
606
|
+
- Apply principles to React components (SRP, DRY)
|
|
607
|
+
|
|
608
|
+
### Java
|
|
609
|
+
- Abstract classes and interfaces for SOLID
|
|
610
|
+
- Annotations for dependency injection (DIP)
|
|
611
|
+
- Packages for modularity
|
|
612
|
+
- Design patterns (Strategy, Factory) for OCP
|
|
613
|
+
|
|
614
|
+
### Python
|
|
615
|
+
- Duck typing supports ISP naturally
|
|
616
|
+
- Modules and packages for SoC
|
|
617
|
+
- Decorators for cross-cutting concerns
|
|
618
|
+
- ABC (Abstract Base Classes) for interfaces
|
|
619
|
+
|
|
620
|
+
### Go
|
|
621
|
+
- Interfaces for DIP and ISP
|
|
622
|
+
- Packages for modularity
|
|
623
|
+
- Composition over inheritance
|
|
624
|
+
- Simple, explicit code (KISS)
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
|
|
628
|
+
## References and Further Reading
|
|
629
|
+
|
|
630
|
+
- **SOLID**: Robert C. Martin (Uncle Bob) - "Clean Architecture"
|
|
631
|
+
- **DRY**: Andy Hunt & Dave Thomas - "The Pragmatic Programmer"
|
|
632
|
+
- **KISS**: Kelly Johnson - Lockheed Skunk Works
|
|
633
|
+
- **YAGNI**: Extreme Programming (XP) practices
|
|
634
|
+
- **Separation of Concerns**: Edsger W. Dijkstra
|
|
635
|
+
- **Modularity**: David Parnas - "On the Criteria to be Used in Decomposing Systems into Modules"
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
**Remember**: These principles are guidelines, not absolute rules. Use judgment to apply them appropriately for your context. When principles conflict, prioritize based on your project's specific needs.
|