@defai.digital/automatosx 5.0.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/CHANGELOG.md +2877 -0
- package/CONTRIBUTING.md +357 -0
- package/FAQ.md +604 -0
- package/FIXES.md +277 -0
- package/LICENSE +190 -0
- package/README.md +603 -0
- package/REVIEW-REPORT.md +278 -0
- package/TROUBLESHOOTING.md +612 -0
- package/automatosx.config.json +219 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11806 -0
- package/dist/index.js.map +1 -0
- package/docs/README.md +227 -0
- package/docs/guide/core-concepts.md +568 -0
- package/docs/guide/installation.md +406 -0
- package/docs/guide/introduction.md +199 -0
- package/docs/guide/quick-start.md +387 -0
- package/docs/index.md +132 -0
- package/docs/reference/cli-commands.md +894 -0
- package/docs/tutorials/first-agent.md +691 -0
- package/docs/tutorials/memory-management.md +785 -0
- package/examples/AGENTS_INFO.md +293 -0
- package/examples/README.md +434 -0
- package/examples/abilities/best-practices.md +102 -0
- package/examples/abilities/code-generation.md +1035 -0
- package/examples/abilities/code-review.md +42 -0
- package/examples/abilities/content-creation.md +97 -0
- package/examples/abilities/debugging.md +43 -0
- package/examples/abilities/documentation.md +54 -0
- package/examples/abilities/error-analysis.md +107 -0
- package/examples/abilities/general-assistance.md +26 -0
- package/examples/abilities/our-architecture-decisions.md +242 -0
- package/examples/abilities/our-code-review-checklist.md +217 -0
- package/examples/abilities/our-coding-standards.md +389 -0
- package/examples/abilities/our-project-structure.md +502 -0
- package/examples/abilities/performance-analysis.md +56 -0
- package/examples/abilities/problem-solving.md +50 -0
- package/examples/abilities/refactoring.md +49 -0
- package/examples/abilities/security-audit.md +65 -0
- package/examples/abilities/task-planning.md +65 -0
- package/examples/abilities/technical-writing.md +77 -0
- package/examples/abilities/testing.md +47 -0
- package/examples/abilities/troubleshooting.md +80 -0
- package/examples/agents/assistant.yaml +45 -0
- package/examples/agents/backend.yaml +60 -0
- package/examples/agents/ceo.yaml +47 -0
- package/examples/agents/coder.yaml +388 -0
- package/examples/agents/cto.yaml +47 -0
- package/examples/agents/data.yaml +47 -0
- package/examples/agents/debugger.yaml +59 -0
- package/examples/agents/design.yaml +46 -0
- package/examples/agents/devops.yaml +47 -0
- package/examples/agents/frontend.yaml +61 -0
- package/examples/agents/product.yaml +47 -0
- package/examples/agents/quality.yaml +47 -0
- package/examples/agents/reviewer.yaml +49 -0
- package/examples/agents/security.yaml +47 -0
- package/examples/agents/writer.yaml +66 -0
- package/examples/claude/commands/ax:agent.md +37 -0
- package/examples/claude/commands/ax:clear.md +22 -0
- package/examples/claude/commands/ax:init.md +25 -0
- package/examples/claude/commands/ax:list.md +19 -0
- package/examples/claude/commands/ax:memory.md +25 -0
- package/examples/claude/commands/ax:status.md +24 -0
- package/examples/claude/commands/ax:update.md +28 -0
- package/examples/claude/mcp/automatosx.json +74 -0
- package/examples/templates/analyst.yaml +60 -0
- package/examples/templates/basic-agent.yaml +28 -0
- package/examples/templates/designer.yaml +69 -0
- package/examples/templates/developer.yaml +60 -0
- package/examples/templates/qa-specialist.yaml +71 -0
- package/examples/use-cases/01-web-app-development.md +374 -0
- package/package.json +86 -0
- package/scripts/check-release.js +128 -0
- package/scripts/real-provider-test.sh +357 -0
- package/scripts/smoke-test.sh +286 -0
- package/tsup.config.ts +16 -0
|
@@ -0,0 +1,1035 @@
|
|
|
1
|
+
# Code Generation Mastery
|
|
2
|
+
|
|
3
|
+
Master the art of generating high-quality, production-ready code across multiple languages and paradigms. This ability covers language-specific patterns, best practices, error handling, testing, and real-world code examples.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [TypeScript/JavaScript Patterns](#typescript-javascript-patterns)
|
|
8
|
+
2. [Python Best Practices](#python-best-practices)
|
|
9
|
+
3. [Error Handling Strategies](#error-handling-strategies)
|
|
10
|
+
4. [API Design Patterns](#api-design-patterns)
|
|
11
|
+
5. [Testing Code Generation](#testing-code-generation)
|
|
12
|
+
6. [Common Pitfalls](#common-pitfalls)
|
|
13
|
+
7. [Quick Reference](#quick-reference)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## TypeScript/JavaScript Patterns
|
|
18
|
+
|
|
19
|
+
### Type-Safe Function Design
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// ✅ Good: Type-safe with proper error handling
|
|
23
|
+
async function fetchUser(id: string): Promise<User> {
|
|
24
|
+
if (!id || id.trim() === '') {
|
|
25
|
+
throw new Error('User ID is required');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(`/api/users/${id}`);
|
|
30
|
+
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
return validateUser(data); // Validate response structure
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error('Failed to fetch user:', error);
|
|
39
|
+
throw new Error(`Failed to fetch user ${id}: ${(error as Error).message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ❌ Bad: No types, poor error handling, no validation
|
|
44
|
+
async function fetchUser(id) {
|
|
45
|
+
return await fetch(`/api/users/${id}`).then(r => r.json());
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**💡 Why the good example is better:**
|
|
50
|
+
|
|
51
|
+
- ✅ Type safety: `Promise<User>` ensures return type
|
|
52
|
+
- ✅ Input validation: Checks for empty/invalid ID
|
|
53
|
+
- ✅ Error handling: Try-catch with specific error messages
|
|
54
|
+
- ✅ HTTP status check: Validates response before parsing
|
|
55
|
+
- ✅ Data validation: `validateUser()` ensures correct structure
|
|
56
|
+
- ✅ Error context: Logs and wraps errors with context
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
### Interface and Type Design
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// ✅ Good: Well-designed types with documentation
|
|
64
|
+
/**
|
|
65
|
+
* User profile data
|
|
66
|
+
*/
|
|
67
|
+
interface User {
|
|
68
|
+
id: string;
|
|
69
|
+
email: string;
|
|
70
|
+
name: string;
|
|
71
|
+
role: UserRole;
|
|
72
|
+
createdAt: Date;
|
|
73
|
+
updatedAt: Date;
|
|
74
|
+
metadata?: Record<string, unknown>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* User role enumeration
|
|
79
|
+
*/
|
|
80
|
+
enum UserRole {
|
|
81
|
+
ADMIN = 'admin',
|
|
82
|
+
USER = 'user',
|
|
83
|
+
GUEST = 'guest'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create user request payload
|
|
88
|
+
*/
|
|
89
|
+
interface CreateUserRequest {
|
|
90
|
+
email: string;
|
|
91
|
+
name: string;
|
|
92
|
+
role?: UserRole; // Optional, defaults to USER
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Update user request payload
|
|
97
|
+
*/
|
|
98
|
+
type UpdateUserRequest = Partial<Omit<User, 'id' | 'createdAt'>>;
|
|
99
|
+
|
|
100
|
+
// ❌ Bad: Vague types, no documentation
|
|
101
|
+
interface UserData {
|
|
102
|
+
data: any;
|
|
103
|
+
stuff: object;
|
|
104
|
+
things?: string[];
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**💡 Best Practices:**
|
|
109
|
+
|
|
110
|
+
- ✅ Use descriptive interface names (User, not UserData)
|
|
111
|
+
- ✅ Document with JSDoc comments
|
|
112
|
+
- ✅ Use enums for fixed values (UserRole)
|
|
113
|
+
- ✅ Use utility types (Partial, Omit, Pick) for variations
|
|
114
|
+
- ✅ Avoid `any` and `object` - be specific
|
|
115
|
+
- ✅ Mark optional fields explicitly with `?`
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### Class Design with SOLID Principles
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// ✅ Good: Single Responsibility, Dependency Injection
|
|
123
|
+
/**
|
|
124
|
+
* User repository for database operations
|
|
125
|
+
*/
|
|
126
|
+
class UserRepository {
|
|
127
|
+
constructor(private db: Database) {}
|
|
128
|
+
|
|
129
|
+
async findById(id: string): Promise<User | null> {
|
|
130
|
+
const row = await this.db.query(
|
|
131
|
+
'SELECT * FROM users WHERE id = ?',
|
|
132
|
+
[id]
|
|
133
|
+
);
|
|
134
|
+
return row ? this.mapToUser(row) : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async create(data: CreateUserRequest): Promise<User> {
|
|
138
|
+
const now = new Date();
|
|
139
|
+
const user: User = {
|
|
140
|
+
id: generateId(),
|
|
141
|
+
email: data.email,
|
|
142
|
+
name: data.name,
|
|
143
|
+
role: data.role ?? UserRole.USER,
|
|
144
|
+
createdAt: now,
|
|
145
|
+
updatedAt: now
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
await this.db.query(
|
|
149
|
+
'INSERT INTO users (id, email, name, role, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)',
|
|
150
|
+
[user.id, user.email, user.name, user.role, user.createdAt, user.updatedAt]
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
return user;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private mapToUser(row: DatabaseRow): User {
|
|
157
|
+
return {
|
|
158
|
+
id: row.id,
|
|
159
|
+
email: row.email,
|
|
160
|
+
name: row.name,
|
|
161
|
+
role: row.role as UserRole,
|
|
162
|
+
createdAt: new Date(row.created_at),
|
|
163
|
+
updatedAt: new Date(row.updated_at)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* User service for business logic
|
|
170
|
+
*/
|
|
171
|
+
class UserService {
|
|
172
|
+
constructor(
|
|
173
|
+
private userRepo: UserRepository,
|
|
174
|
+
private emailService: EmailService
|
|
175
|
+
) {}
|
|
176
|
+
|
|
177
|
+
async registerUser(data: CreateUserRequest): Promise<User> {
|
|
178
|
+
// Validation
|
|
179
|
+
if (!this.isValidEmail(data.email)) {
|
|
180
|
+
throw new Error('Invalid email format');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check if user exists
|
|
184
|
+
const existing = await this.userRepo.findByEmail(data.email);
|
|
185
|
+
if (existing) {
|
|
186
|
+
throw new Error('Email already registered');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Create user
|
|
190
|
+
const user = await this.userRepo.create(data);
|
|
191
|
+
|
|
192
|
+
// Send welcome email (async, don't block)
|
|
193
|
+
this.emailService.sendWelcome(user.email).catch(error => {
|
|
194
|
+
console.error('Failed to send welcome email:', error);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return user;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private isValidEmail(email: string): boolean {
|
|
201
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
202
|
+
return emailRegex.test(email);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ❌ Bad: God class, tight coupling, mixed concerns
|
|
207
|
+
class UserManager {
|
|
208
|
+
async doEverything(email, name) {
|
|
209
|
+
// Database access mixed with business logic
|
|
210
|
+
const db = new Database();
|
|
211
|
+
const user = db.query('INSERT INTO users...');
|
|
212
|
+
|
|
213
|
+
// Email sending mixed with user creation
|
|
214
|
+
const smtp = new SmtpClient();
|
|
215
|
+
smtp.send(email, 'Welcome');
|
|
216
|
+
|
|
217
|
+
return user;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**💡 SOLID Principles Applied:**
|
|
223
|
+
|
|
224
|
+
- ✅ **S**ingle Responsibility: UserRepository (data), UserService (logic)
|
|
225
|
+
- ✅ **O**pen/Closed: Extensible through interfaces
|
|
226
|
+
- ✅ **L**iskov Substitution: Can swap Database implementations
|
|
227
|
+
- ✅ **I**nterface Segregation: Small, focused interfaces
|
|
228
|
+
- ✅ **D**ependency Inversion: Depend on abstractions (Database interface)
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Python Best Practices
|
|
233
|
+
|
|
234
|
+
### Type Hints and Validation
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
# ✅ Good: Type hints with runtime validation
|
|
238
|
+
from typing import Optional, List, Dict, Any
|
|
239
|
+
from dataclasses import dataclass
|
|
240
|
+
from datetime import datetime
|
|
241
|
+
import re
|
|
242
|
+
|
|
243
|
+
@dataclass
|
|
244
|
+
class User:
|
|
245
|
+
"""User profile data"""
|
|
246
|
+
id: str
|
|
247
|
+
email: str
|
|
248
|
+
name: str
|
|
249
|
+
role: str
|
|
250
|
+
created_at: datetime
|
|
251
|
+
updated_at: datetime
|
|
252
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
253
|
+
|
|
254
|
+
def __post_init__(self):
|
|
255
|
+
"""Validate data after initialization"""
|
|
256
|
+
if not self.id:
|
|
257
|
+
raise ValueError("User ID is required")
|
|
258
|
+
if not self._is_valid_email(self.email):
|
|
259
|
+
raise ValueError(f"Invalid email format: {self.email}")
|
|
260
|
+
if self.role not in ['admin', 'user', 'guest']:
|
|
261
|
+
raise ValueError(f"Invalid role: {self.role}")
|
|
262
|
+
|
|
263
|
+
@staticmethod
|
|
264
|
+
def _is_valid_email(email: str) -> bool:
|
|
265
|
+
"""Validate email format"""
|
|
266
|
+
pattern = r'^[^\s@]+@[^\s@]+\.[^\s@]+$'
|
|
267
|
+
return re.match(pattern, email) is not None
|
|
268
|
+
|
|
269
|
+
class UserRepository:
|
|
270
|
+
"""User repository for database operations"""
|
|
271
|
+
|
|
272
|
+
def __init__(self, db_connection):
|
|
273
|
+
self._db = db_connection
|
|
274
|
+
|
|
275
|
+
def find_by_id(self, user_id: str) -> Optional[User]:
|
|
276
|
+
"""
|
|
277
|
+
Find user by ID
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
user_id: User ID to search for
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
User object if found, None otherwise
|
|
284
|
+
|
|
285
|
+
Raises:
|
|
286
|
+
DatabaseError: If database query fails
|
|
287
|
+
"""
|
|
288
|
+
try:
|
|
289
|
+
row = self._db.query(
|
|
290
|
+
"SELECT * FROM users WHERE id = ?",
|
|
291
|
+
(user_id,)
|
|
292
|
+
)
|
|
293
|
+
return self._map_to_user(row) if row else None
|
|
294
|
+
except Exception as e:
|
|
295
|
+
raise DatabaseError(f"Failed to find user {user_id}") from e
|
|
296
|
+
|
|
297
|
+
def create(self, email: str, name: str, role: str = 'user') -> User:
|
|
298
|
+
"""
|
|
299
|
+
Create new user
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
email: User email address
|
|
303
|
+
name: User full name
|
|
304
|
+
role: User role (default: 'user')
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
Created User object
|
|
308
|
+
|
|
309
|
+
Raises:
|
|
310
|
+
ValueError: If validation fails
|
|
311
|
+
DatabaseError: If database insert fails
|
|
312
|
+
"""
|
|
313
|
+
now = datetime.utcnow()
|
|
314
|
+
user = User(
|
|
315
|
+
id=self._generate_id(),
|
|
316
|
+
email=email,
|
|
317
|
+
name=name,
|
|
318
|
+
role=role,
|
|
319
|
+
created_at=now,
|
|
320
|
+
updated_at=now
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
self._db.execute(
|
|
325
|
+
"INSERT INTO users (id, email, name, role, created_at, updated_at) "
|
|
326
|
+
"VALUES (?, ?, ?, ?, ?, ?)",
|
|
327
|
+
(user.id, user.email, user.name, user.role, user.created_at, user.updated_at)
|
|
328
|
+
)
|
|
329
|
+
self._db.commit()
|
|
330
|
+
except Exception as e:
|
|
331
|
+
self._db.rollback()
|
|
332
|
+
raise DatabaseError(f"Failed to create user") from e
|
|
333
|
+
|
|
334
|
+
return user
|
|
335
|
+
|
|
336
|
+
def _map_to_user(self, row: Dict[str, Any]) -> User:
|
|
337
|
+
"""Map database row to User object"""
|
|
338
|
+
return User(
|
|
339
|
+
id=row['id'],
|
|
340
|
+
email=row['email'],
|
|
341
|
+
name=row['name'],
|
|
342
|
+
role=row['role'],
|
|
343
|
+
created_at=datetime.fromisoformat(row['created_at']),
|
|
344
|
+
updated_at=datetime.fromisoformat(row['updated_at']),
|
|
345
|
+
metadata=row.get('metadata')
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
@staticmethod
|
|
349
|
+
def _generate_id() -> str:
|
|
350
|
+
"""Generate unique user ID"""
|
|
351
|
+
import uuid
|
|
352
|
+
return str(uuid.uuid4())
|
|
353
|
+
|
|
354
|
+
# ❌ Bad: No types, no validation, poor error handling
|
|
355
|
+
class UserRepo:
|
|
356
|
+
def get_user(self, id):
|
|
357
|
+
return self.db.query("SELECT * FROM users WHERE id = " + id) # SQL injection!
|
|
358
|
+
|
|
359
|
+
def add_user(self, data):
|
|
360
|
+
self.db.insert(data) # No validation, no error handling
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**💡 Python Best Practices:**
|
|
364
|
+
|
|
365
|
+
- ✅ Type hints for all parameters and returns
|
|
366
|
+
- ✅ Dataclasses for simple data structures
|
|
367
|
+
- ✅ `__post_init__` for validation
|
|
368
|
+
- ✅ Docstrings with Args/Returns/Raises
|
|
369
|
+
- ✅ Use `from ... import Error` for exception chaining
|
|
370
|
+
- ✅ Parameterized queries (prevent SQL injection)
|
|
371
|
+
- ✅ Transaction management (commit/rollback)
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Error Handling Strategies
|
|
376
|
+
|
|
377
|
+
### Explicit Error Handling (TypeScript)
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
// ✅ Good: Explicit error types with context
|
|
381
|
+
class ValidationError extends Error {
|
|
382
|
+
constructor(
|
|
383
|
+
message: string,
|
|
384
|
+
public field: string,
|
|
385
|
+
public value: any
|
|
386
|
+
) {
|
|
387
|
+
super(message);
|
|
388
|
+
this.name = 'ValidationError';
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
class NotFoundError extends Error {
|
|
393
|
+
constructor(
|
|
394
|
+
message: string,
|
|
395
|
+
public resource: string,
|
|
396
|
+
public id: string
|
|
397
|
+
) {
|
|
398
|
+
super(message);
|
|
399
|
+
this.name = 'NotFoundError';
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
class UserService {
|
|
404
|
+
async getUser(id: string): Promise<User> {
|
|
405
|
+
// Input validation
|
|
406
|
+
if (!id || id.trim() === '') {
|
|
407
|
+
throw new ValidationError(
|
|
408
|
+
'User ID is required',
|
|
409
|
+
'id',
|
|
410
|
+
id
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Fetch user
|
|
415
|
+
const user = await this.userRepo.findById(id);
|
|
416
|
+
|
|
417
|
+
if (!user) {
|
|
418
|
+
throw new NotFoundError(
|
|
419
|
+
`User not found`,
|
|
420
|
+
'User',
|
|
421
|
+
id
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return user;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async createUser(data: CreateUserRequest): Promise<User> {
|
|
429
|
+
try {
|
|
430
|
+
// Validate email
|
|
431
|
+
if (!this.isValidEmail(data.email)) {
|
|
432
|
+
throw new ValidationError(
|
|
433
|
+
'Invalid email format',
|
|
434
|
+
'email',
|
|
435
|
+
data.email
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Check for duplicates
|
|
440
|
+
const existing = await this.userRepo.findByEmail(data.email);
|
|
441
|
+
if (existing) {
|
|
442
|
+
throw new ValidationError(
|
|
443
|
+
'Email already registered',
|
|
444
|
+
'email',
|
|
445
|
+
data.email
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Create user
|
|
450
|
+
return await this.userRepo.create(data);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
// Re-throw known errors
|
|
453
|
+
if (error instanceof ValidationError || error instanceof NotFoundError) {
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Wrap unknown errors
|
|
458
|
+
throw new Error(
|
|
459
|
+
`Failed to create user: ${(error as Error).message}`
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// API layer error handling
|
|
466
|
+
async function handleCreateUser(req: Request, res: Response) {
|
|
467
|
+
try {
|
|
468
|
+
const user = await userService.createUser(req.body);
|
|
469
|
+
res.status(201).json(user);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
if (error instanceof ValidationError) {
|
|
472
|
+
res.status(400).json({
|
|
473
|
+
error: 'Validation failed',
|
|
474
|
+
field: error.field,
|
|
475
|
+
message: error.message
|
|
476
|
+
});
|
|
477
|
+
} else if (error instanceof NotFoundError) {
|
|
478
|
+
res.status(404).json({
|
|
479
|
+
error: 'Not found',
|
|
480
|
+
resource: error.resource,
|
|
481
|
+
message: error.message
|
|
482
|
+
});
|
|
483
|
+
} else {
|
|
484
|
+
console.error('Unexpected error:', error);
|
|
485
|
+
res.status(500).json({
|
|
486
|
+
error: 'Internal server error',
|
|
487
|
+
message: 'An unexpected error occurred'
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// ❌ Bad: Generic errors with no context
|
|
494
|
+
async function getUser(id) {
|
|
495
|
+
const user = await db.query(...);
|
|
496
|
+
if (!user) {
|
|
497
|
+
throw new Error('Error'); // No context!
|
|
498
|
+
}
|
|
499
|
+
return user;
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**💡 Error Handling Principles:**
|
|
504
|
+
|
|
505
|
+
- ✅ Create custom error classes for different scenarios
|
|
506
|
+
- ✅ Include context (field, value, resource, id)
|
|
507
|
+
- ✅ Re-throw known errors, wrap unknown errors
|
|
508
|
+
- ✅ Map errors to appropriate HTTP status codes
|
|
509
|
+
- ✅ Don't expose internal errors to clients
|
|
510
|
+
- ✅ Log unexpected errors for debugging
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## API Design Patterns
|
|
515
|
+
|
|
516
|
+
### RESTful API Design
|
|
517
|
+
|
|
518
|
+
```typescript
|
|
519
|
+
// ✅ Good: RESTful API with proper structure
|
|
520
|
+
import express, { Router } from 'express';
|
|
521
|
+
import { body, param, validationResult } from 'express-validator';
|
|
522
|
+
|
|
523
|
+
const router = Router();
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* GET /api/users/:id
|
|
527
|
+
* Get user by ID
|
|
528
|
+
*/
|
|
529
|
+
router.get(
|
|
530
|
+
'/users/:id',
|
|
531
|
+
[
|
|
532
|
+
param('id').isUUID().withMessage('Invalid user ID format')
|
|
533
|
+
],
|
|
534
|
+
async (req, res, next) => {
|
|
535
|
+
try {
|
|
536
|
+
// Validation
|
|
537
|
+
const errors = validationResult(req);
|
|
538
|
+
if (!errors.isEmpty()) {
|
|
539
|
+
return res.status(400).json({ errors: errors.array() });
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Fetch user
|
|
543
|
+
const user = await userService.getUser(req.params.id);
|
|
544
|
+
|
|
545
|
+
res.json({
|
|
546
|
+
data: user,
|
|
547
|
+
meta: {
|
|
548
|
+
requestId: req.id,
|
|
549
|
+
timestamp: new Date().toISOString()
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
} catch (error) {
|
|
553
|
+
next(error); // Pass to error handler middleware
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* POST /api/users
|
|
560
|
+
* Create new user
|
|
561
|
+
*/
|
|
562
|
+
router.post(
|
|
563
|
+
'/users',
|
|
564
|
+
[
|
|
565
|
+
body('email').isEmail().withMessage('Invalid email format'),
|
|
566
|
+
body('name').trim().notEmpty().withMessage('Name is required'),
|
|
567
|
+
body('role').optional().isIn(['admin', 'user', 'guest']).withMessage('Invalid role')
|
|
568
|
+
],
|
|
569
|
+
async (req, res, next) => {
|
|
570
|
+
try {
|
|
571
|
+
// Validation
|
|
572
|
+
const errors = validationResult(req);
|
|
573
|
+
if (!errors.isEmpty()) {
|
|
574
|
+
return res.status(400).json({ errors: errors.array() });
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Create user
|
|
578
|
+
const user = await userService.createUser(req.body);
|
|
579
|
+
|
|
580
|
+
res.status(201).json({
|
|
581
|
+
data: user,
|
|
582
|
+
meta: {
|
|
583
|
+
requestId: req.id,
|
|
584
|
+
timestamp: new Date().toISOString()
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
} catch (error) {
|
|
588
|
+
next(error);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* PATCH /api/users/:id
|
|
595
|
+
* Update user (partial)
|
|
596
|
+
*/
|
|
597
|
+
router.patch(
|
|
598
|
+
'/users/:id',
|
|
599
|
+
[
|
|
600
|
+
param('id').isUUID(),
|
|
601
|
+
body('email').optional().isEmail(),
|
|
602
|
+
body('name').optional().trim().notEmpty(),
|
|
603
|
+
body('role').optional().isIn(['admin', 'user', 'guest'])
|
|
604
|
+
],
|
|
605
|
+
async (req, res, next) => {
|
|
606
|
+
try {
|
|
607
|
+
const errors = validationResult(req);
|
|
608
|
+
if (!errors.isEmpty()) {
|
|
609
|
+
return res.status(400).json({ errors: errors.array() });
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const user = await userService.updateUser(req.params.id, req.body);
|
|
613
|
+
|
|
614
|
+
res.json({
|
|
615
|
+
data: user,
|
|
616
|
+
meta: {
|
|
617
|
+
requestId: req.id,
|
|
618
|
+
timestamp: new Date().toISOString()
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
} catch (error) {
|
|
622
|
+
next(error);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* DELETE /api/users/:id
|
|
629
|
+
* Delete user
|
|
630
|
+
*/
|
|
631
|
+
router.delete(
|
|
632
|
+
'/users/:id',
|
|
633
|
+
[
|
|
634
|
+
param('id').isUUID()
|
|
635
|
+
],
|
|
636
|
+
async (req, res, next) => {
|
|
637
|
+
try {
|
|
638
|
+
const errors = validationResult(req);
|
|
639
|
+
if (!errors.isEmpty()) {
|
|
640
|
+
return res.status(400).json({ errors: errors.array() });
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
await userService.deleteUser(req.params.id);
|
|
644
|
+
|
|
645
|
+
res.status(204).send(); // No content
|
|
646
|
+
} catch (error) {
|
|
647
|
+
next(error);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
// Error handler middleware
|
|
653
|
+
router.use((error: Error, req: Request, res: Response, next: NextFunction) => {
|
|
654
|
+
if (error instanceof ValidationError) {
|
|
655
|
+
return res.status(400).json({
|
|
656
|
+
error: {
|
|
657
|
+
type: 'ValidationError',
|
|
658
|
+
message: error.message,
|
|
659
|
+
field: error.field
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (error instanceof NotFoundError) {
|
|
665
|
+
return res.status(404).json({
|
|
666
|
+
error: {
|
|
667
|
+
type: 'NotFoundError',
|
|
668
|
+
message: error.message,
|
|
669
|
+
resource: error.resource
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Unexpected errors
|
|
675
|
+
console.error('Unexpected error:', error);
|
|
676
|
+
res.status(500).json({
|
|
677
|
+
error: {
|
|
678
|
+
type: 'InternalError',
|
|
679
|
+
message: 'An unexpected error occurred'
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
// ❌ Bad: Inconsistent, no validation, poor error handling
|
|
685
|
+
router.get('/getUser', async (req, res) => {
|
|
686
|
+
const user = await db.query('SELECT * FROM users WHERE id = ' + req.query.id); // SQL injection!
|
|
687
|
+
res.send(user);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
router.post('/user/create', async (req, res) => {
|
|
691
|
+
const user = await db.insert(req.body); // No validation!
|
|
692
|
+
res.send({ success: true, user });
|
|
693
|
+
});
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**💡 RESTful API Best Practices:**
|
|
697
|
+
|
|
698
|
+
- ✅ Use proper HTTP methods (GET, POST, PATCH, DELETE)
|
|
699
|
+
- ✅ Use resource-based URLs (`/users/:id`, not `/getUser`)
|
|
700
|
+
- ✅ Validate all inputs with express-validator
|
|
701
|
+
- ✅ Return appropriate HTTP status codes (200, 201, 400, 404, 500)
|
|
702
|
+
- ✅ Use consistent response structure (`{ data, meta }`)
|
|
703
|
+
- ✅ Centralized error handling middleware
|
|
704
|
+
- ✅ Return 204 No Content for DELETE
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
## Testing Code Generation
|
|
709
|
+
|
|
710
|
+
### Unit Testing with TDD
|
|
711
|
+
|
|
712
|
+
```typescript
|
|
713
|
+
// ✅ Good: Comprehensive tests with edge cases
|
|
714
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
715
|
+
import { UserService } from './user-service';
|
|
716
|
+
import { UserRepository } from './user-repository';
|
|
717
|
+
import { ValidationError, NotFoundError } from './errors';
|
|
718
|
+
|
|
719
|
+
describe('UserService', () => {
|
|
720
|
+
let userService: UserService;
|
|
721
|
+
let mockUserRepo: jest.Mocked<UserRepository>;
|
|
722
|
+
|
|
723
|
+
beforeEach(() => {
|
|
724
|
+
// Create mock repository
|
|
725
|
+
mockUserRepo = {
|
|
726
|
+
findById: vi.fn(),
|
|
727
|
+
findByEmail: vi.fn(),
|
|
728
|
+
create: vi.fn(),
|
|
729
|
+
update: vi.fn(),
|
|
730
|
+
delete: vi.fn()
|
|
731
|
+
} as any;
|
|
732
|
+
|
|
733
|
+
userService = new UserService(mockUserRepo);
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
describe('getUser', () => {
|
|
737
|
+
it('should return user when found', async () => {
|
|
738
|
+
// Arrange
|
|
739
|
+
const mockUser = {
|
|
740
|
+
id: '123',
|
|
741
|
+
email: 'test@example.com',
|
|
742
|
+
name: 'Test User',
|
|
743
|
+
role: 'user'
|
|
744
|
+
};
|
|
745
|
+
mockUserRepo.findById.mockResolvedValue(mockUser);
|
|
746
|
+
|
|
747
|
+
// Act
|
|
748
|
+
const result = await userService.getUser('123');
|
|
749
|
+
|
|
750
|
+
// Assert
|
|
751
|
+
expect(result).toEqual(mockUser);
|
|
752
|
+
expect(mockUserRepo.findById).toHaveBeenCalledWith('123');
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
it('should throw NotFoundError when user not found', async () => {
|
|
756
|
+
// Arrange
|
|
757
|
+
mockUserRepo.findById.mockResolvedValue(null);
|
|
758
|
+
|
|
759
|
+
// Act & Assert
|
|
760
|
+
await expect(userService.getUser('999'))
|
|
761
|
+
.rejects
|
|
762
|
+
.toThrow(NotFoundError);
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
it('should throw ValidationError for empty ID', async () => {
|
|
766
|
+
// Act & Assert
|
|
767
|
+
await expect(userService.getUser(''))
|
|
768
|
+
.rejects
|
|
769
|
+
.toThrow(ValidationError);
|
|
770
|
+
|
|
771
|
+
// Should not call repository for invalid input
|
|
772
|
+
expect(mockUserRepo.findById).not.toHaveBeenCalled();
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
it('should throw ValidationError for whitespace ID', async () => {
|
|
776
|
+
await expect(userService.getUser(' '))
|
|
777
|
+
.rejects
|
|
778
|
+
.toThrow(ValidationError);
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
describe('createUser', () => {
|
|
783
|
+
it('should create user with valid data', async () => {
|
|
784
|
+
// Arrange
|
|
785
|
+
const createData = {
|
|
786
|
+
email: 'new@example.com',
|
|
787
|
+
name: 'New User',
|
|
788
|
+
role: 'user'
|
|
789
|
+
};
|
|
790
|
+
const mockCreatedUser = { id: '456', ...createData };
|
|
791
|
+
|
|
792
|
+
mockUserRepo.findByEmail.mockResolvedValue(null); // No duplicate
|
|
793
|
+
mockUserRepo.create.mockResolvedValue(mockCreatedUser);
|
|
794
|
+
|
|
795
|
+
// Act
|
|
796
|
+
const result = await userService.createUser(createData);
|
|
797
|
+
|
|
798
|
+
// Assert
|
|
799
|
+
expect(result).toEqual(mockCreatedUser);
|
|
800
|
+
expect(mockUserRepo.findByEmail).toHaveBeenCalledWith('new@example.com');
|
|
801
|
+
expect(mockUserRepo.create).toHaveBeenCalledWith(createData);
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
it('should throw ValidationError for invalid email', async () => {
|
|
805
|
+
// Arrange
|
|
806
|
+
const invalidData = {
|
|
807
|
+
email: 'not-an-email',
|
|
808
|
+
name: 'Test',
|
|
809
|
+
role: 'user'
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
// Act & Assert
|
|
813
|
+
await expect(userService.createUser(invalidData))
|
|
814
|
+
.rejects
|
|
815
|
+
.toThrow(ValidationError);
|
|
816
|
+
|
|
817
|
+
expect(mockUserRepo.create).not.toHaveBeenCalled();
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
it('should throw ValidationError for duplicate email', async () => {
|
|
821
|
+
// Arrange
|
|
822
|
+
const duplicateData = {
|
|
823
|
+
email: 'existing@example.com',
|
|
824
|
+
name: 'Test',
|
|
825
|
+
role: 'user'
|
|
826
|
+
};
|
|
827
|
+
mockUserRepo.findByEmail.mockResolvedValue({ id: '123' } as any);
|
|
828
|
+
|
|
829
|
+
// Act & Assert
|
|
830
|
+
await expect(userService.createUser(duplicateData))
|
|
831
|
+
.rejects
|
|
832
|
+
.toThrow(ValidationError);
|
|
833
|
+
|
|
834
|
+
expect(mockUserRepo.create).not.toHaveBeenCalled();
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it('should use default role when not provided', async () => {
|
|
838
|
+
// Arrange
|
|
839
|
+
const dataWithoutRole = {
|
|
840
|
+
email: 'test@example.com',
|
|
841
|
+
name: 'Test User'
|
|
842
|
+
};
|
|
843
|
+
mockUserRepo.findByEmail.mockResolvedValue(null);
|
|
844
|
+
mockUserRepo.create.mockResolvedValue({ id: '123', ...dataWithoutRole, role: 'user' } as any);
|
|
845
|
+
|
|
846
|
+
// Act
|
|
847
|
+
await userService.createUser(dataWithoutRole);
|
|
848
|
+
|
|
849
|
+
// Assert
|
|
850
|
+
expect(mockUserRepo.create).toHaveBeenCalledWith(
|
|
851
|
+
expect.objectContaining({ role: 'user' })
|
|
852
|
+
);
|
|
853
|
+
});
|
|
854
|
+
});
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
// ❌ Bad: Minimal tests, no edge cases
|
|
858
|
+
describe('UserService', () => {
|
|
859
|
+
it('works', async () => {
|
|
860
|
+
const user = await userService.getUser('123');
|
|
861
|
+
expect(user).toBeDefined();
|
|
862
|
+
});
|
|
863
|
+
});
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
**💡 Testing Best Practices:**
|
|
867
|
+
|
|
868
|
+
- ✅ Use AAA pattern (Arrange, Act, Assert)
|
|
869
|
+
- ✅ Test happy path AND edge cases
|
|
870
|
+
- ✅ Test error conditions
|
|
871
|
+
- ✅ Use descriptive test names (`should ... when ...`)
|
|
872
|
+
- ✅ Mock external dependencies
|
|
873
|
+
- ✅ Verify mock calls with `toHaveBeenCalledWith`
|
|
874
|
+
- ✅ Test default values and optional parameters
|
|
875
|
+
|
|
876
|
+
---
|
|
877
|
+
|
|
878
|
+
## Common Pitfalls
|
|
879
|
+
|
|
880
|
+
### Pitfall 1: Ignoring Edge Cases
|
|
881
|
+
|
|
882
|
+
```typescript
|
|
883
|
+
// ❌ Bad: No edge case handling
|
|
884
|
+
function divide(a: number, b: number): number {
|
|
885
|
+
return a / b; // What if b is 0?
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// ✅ Good: Handle edge cases explicitly
|
|
889
|
+
function divide(a: number, b: number): number {
|
|
890
|
+
if (b === 0) {
|
|
891
|
+
throw new Error('Division by zero');
|
|
892
|
+
}
|
|
893
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) {
|
|
894
|
+
throw new Error('Invalid input: must be finite numbers');
|
|
895
|
+
}
|
|
896
|
+
return a / b;
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
### Pitfall 2: Silent Failures
|
|
901
|
+
|
|
902
|
+
```typescript
|
|
903
|
+
// ❌ Bad: Silent failure
|
|
904
|
+
async function updateUser(id: string, data: any) {
|
|
905
|
+
try {
|
|
906
|
+
await userRepo.update(id, data);
|
|
907
|
+
} catch (error) {
|
|
908
|
+
// Silent failure - error swallowed!
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// ✅ Good: Fail loudly with context
|
|
913
|
+
async function updateUser(id: string, data: UpdateUserRequest): Promise<User> {
|
|
914
|
+
try {
|
|
915
|
+
const user = await userRepo.update(id, data);
|
|
916
|
+
return user;
|
|
917
|
+
} catch (error) {
|
|
918
|
+
console.error('Failed to update user:', { id, error });
|
|
919
|
+
throw new Error(`Failed to update user ${id}: ${(error as Error).message}`);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
### Pitfall 3: Overly Complex Functions
|
|
925
|
+
|
|
926
|
+
```typescript
|
|
927
|
+
// ❌ Bad: God function doing everything
|
|
928
|
+
async function handleUserRegistration(email, name, password, address, phone, ...more) {
|
|
929
|
+
// 200 lines of code
|
|
930
|
+
// Validation, hashing, database, email, logging, analytics...
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// ✅ Good: Single Responsibility - delegate to specialized functions
|
|
934
|
+
async function registerUser(data: RegistrationData): Promise<User> {
|
|
935
|
+
// Validate
|
|
936
|
+
validateRegistrationData(data);
|
|
937
|
+
|
|
938
|
+
// Create user
|
|
939
|
+
const user = await createUser({
|
|
940
|
+
email: data.email,
|
|
941
|
+
name: data.name,
|
|
942
|
+
passwordHash: await hashPassword(data.password)
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
// Send welcome email (async, don't block)
|
|
946
|
+
sendWelcomeEmail(user).catch(handleEmailError);
|
|
947
|
+
|
|
948
|
+
// Track analytics (async, don't block)
|
|
949
|
+
trackUserRegistration(user).catch(handleAnalyticsError);
|
|
950
|
+
|
|
951
|
+
return user;
|
|
952
|
+
}
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
### Pitfall 4: Mutation Instead of Immutability
|
|
956
|
+
|
|
957
|
+
```typescript
|
|
958
|
+
// ❌ Bad: Mutates input
|
|
959
|
+
function addItem(array: any[], item: any) {
|
|
960
|
+
array.push(item); // Mutates!
|
|
961
|
+
return array;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// ✅ Good: Returns new array (immutable)
|
|
965
|
+
function addItem<T>(array: T[], item: T): T[] {
|
|
966
|
+
return [...array, item]; // New array
|
|
967
|
+
}
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
---
|
|
971
|
+
|
|
972
|
+
## Quick Reference
|
|
973
|
+
|
|
974
|
+
### Code Generation Checklist
|
|
975
|
+
|
|
976
|
+
**Type Safety:**
|
|
977
|
+
|
|
978
|
+
- [ ] All function parameters have types
|
|
979
|
+
- [ ] All return types are explicitly defined
|
|
980
|
+
- [ ] No use of `any` or `unknown` without justification
|
|
981
|
+
- [ ] Interfaces and types are well-documented
|
|
982
|
+
|
|
983
|
+
**Error Handling:**
|
|
984
|
+
|
|
985
|
+
- [ ] Input validation for all public functions
|
|
986
|
+
- [ ] Explicit error types (custom error classes)
|
|
987
|
+
- [ ] Try-catch blocks for async operations
|
|
988
|
+
- [ ] Errors include context (field, value, id)
|
|
989
|
+
- [ ] No silent failures (always throw or log)
|
|
990
|
+
|
|
991
|
+
**Testing:**
|
|
992
|
+
|
|
993
|
+
- [ ] Unit tests for all public methods
|
|
994
|
+
- [ ] Tests cover happy path + edge cases
|
|
995
|
+
- [ ] Tests verify error conditions
|
|
996
|
+
- [ ] Mocks for external dependencies
|
|
997
|
+
- [ ] Descriptive test names
|
|
998
|
+
|
|
999
|
+
**Code Quality:**
|
|
1000
|
+
|
|
1001
|
+
- [ ] Functions are small and focused (< 50 lines)
|
|
1002
|
+
- [ ] Descriptive variable and function names
|
|
1003
|
+
- [ ] JSDoc/TSDoc comments for public APIs
|
|
1004
|
+
- [ ] No code duplication (DRY)
|
|
1005
|
+
- [ ] Follows SOLID principles
|
|
1006
|
+
|
|
1007
|
+
**API Design:**
|
|
1008
|
+
|
|
1009
|
+
- [ ] RESTful URLs (resource-based)
|
|
1010
|
+
- [ ] Proper HTTP methods (GET, POST, PATCH, DELETE)
|
|
1011
|
+
- [ ] Input validation (express-validator)
|
|
1012
|
+
- [ ] Appropriate HTTP status codes
|
|
1013
|
+
- [ ] Consistent response structure
|
|
1014
|
+
|
|
1015
|
+
---
|
|
1016
|
+
|
|
1017
|
+
## Summary
|
|
1018
|
+
|
|
1019
|
+
Generating high-quality code requires:
|
|
1020
|
+
|
|
1021
|
+
1. **Type Safety** - Use TypeScript, define all types explicitly
|
|
1022
|
+
2. **Error Handling** - Explicit errors, no silent failures, include context
|
|
1023
|
+
3. **Testing** - TDD approach, test happy path + edge cases + errors
|
|
1024
|
+
4. **Code Quality** - SOLID principles, small functions, DRY, descriptive names
|
|
1025
|
+
5. **API Design** - RESTful, validated inputs, appropriate status codes
|
|
1026
|
+
6. **Edge Cases** - Handle division by zero, null/undefined, empty strings, etc.
|
|
1027
|
+
|
|
1028
|
+
**Remember:**
|
|
1029
|
+
|
|
1030
|
+
- Code is read 10x more than written - optimize for readability
|
|
1031
|
+
- Fail fast, fail loudly - explicit errors over silent failures
|
|
1032
|
+
- Tests are documentation that never goes out of date
|
|
1033
|
+
- Simplicity is the ultimate sophistication
|
|
1034
|
+
|
|
1035
|
+
Use this ability to generate production-ready code with proper types, error handling, tests, and documentation.
|