skillstore-cli 1.0.0
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 +95 -0
- package/data/bundles/devflow-complete.json +19 -0
- package/data/free-skills/devflow-agile/manifest.json +19 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/retro.md +23 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/review.md +21 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/sprint.md +30 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/standup.md +20 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile.md +35 -0
- package/data/free-skills/devflow-agile/plugin/commands/devflow.md +42 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/SKILL.md +93 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/assets/sample-output.md +182 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/clean-architecture.md +361 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/clean-code-guide.md +207 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/debugging-methodology.md +191 -0
- package/data/free-skills/devflow-agile/template/agents/agile-coach.md +76 -0
- package/data/free-skills/devflow-agile/template/workflows/agile-sprint-workflow.md +81 -0
- package/data/free-skills/devflow-bootstrap/manifest.json +8 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/bootstrap/auto.md +31 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/bootstrap.md +38 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/devflow.md +20 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/SKILL.md +56 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/assets/sample-output.md +216 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/references/architecture-decisions.md +254 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/references/stack-templates.md +400 -0
- package/data/free-skills/devflow-bootstrap/template/agents/bootstrap-specialist.md +56 -0
- package/data/free-skills/devflow-bootstrap/template/workflows/bootstrap-workflow.md +70 -0
- package/data/free-skills/devflow-docs/manifest.json +8 -0
- package/data/free-skills/devflow-docs/plugin/commands/devflow.md +20 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs/generate.md +17 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs/parse.md +19 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs.md +26 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/SKILL.md +59 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/assets/sample-output.md +114 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/references/extraction-techniques.md +115 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/references/ocr-strategies.md +167 -0
- package/data/free-skills/devflow-docs/template/agents/docs-specialist.md +35 -0
- package/data/free-skills/devflow-docs/template/workflows/docs-workflow.md +70 -0
- package/data/free-skills/devflow-postproject/manifest.json +13 -0
- package/data/free-skills/devflow-postproject/plugin/commands/devflow.md +34 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/handover.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/retro.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/support.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject.md +32 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/SKILL.md +70 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/assets/sample-output.md +79 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/facilitation-techniques.md +178 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/lessons-learned-template.md +118 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/retro-techniques.md +100 -0
- package/data/free-skills/devflow-postproject/template/agents/transition-manager.md +71 -0
- package/data/free-skills/devflow-postproject/template/workflows/transition-workflow.md +72 -0
- package/data/free-skills/devflow-presale/manifest.json +15 -0
- package/data/free-skills/devflow-presale/plugin/commands/devflow.md +47 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/analyze.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/estimate.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/price.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/propose.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale.md +42 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/SKILL.md +63 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/assets/sample-output.md +129 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/references/extraction-framework.md +140 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/references/output-template.md +132 -0
- package/data/free-skills/devflow-presale/template/agents/presale-lead.md +83 -0
- package/data/free-skills/devflow-presale/template/agents/proposal-reviewer.md +63 -0
- package/data/free-skills/devflow-presale/template/workflows/presale-workflow.md +70 -0
- package/data/registry/categories.json +7 -0
- package/data/registry/packages.json +184 -0
- package/data/shared/framework/agents/brainstormer.md +74 -0
- package/data/shared/framework/agents/code-reviewer.md +87 -0
- package/data/shared/framework/agents/debugger.md +84 -0
- package/data/shared/framework/agents/docs-manager.md +55 -0
- package/data/shared/framework/agents/git-manager.md +59 -0
- package/data/shared/framework/agents/planner.md +68 -0
- package/data/shared/framework/agents/researcher.md +66 -0
- package/data/shared/framework/agents/tester.md +65 -0
- package/data/shared/framework/commands/cook/auto.md +27 -0
- package/data/shared/framework/commands/cook.md +45 -0
- package/data/shared/framework/commands/fix/ci.md +21 -0
- package/data/shared/framework/commands/fix/test.md +26 -0
- package/data/shared/framework/commands/fix/types.md +29 -0
- package/data/shared/framework/commands/fix.md +26 -0
- package/data/shared/framework/commands/git/cm.md +37 -0
- package/data/shared/framework/commands/git/pr.md +40 -0
- package/data/shared/framework/config/CLAUDE.md.template +26 -0
- package/data/shared/framework/config/settings.json +41 -0
- package/data/shared/framework/config/skillstore.config.json +29 -0
- package/data/shared/framework/hooks/discord-notify.sh +85 -0
- package/data/shared/framework/hooks/docs-sync.sh +53 -0
- package/data/shared/framework/hooks/modularization-hook.js +103 -0
- package/data/shared/framework/hooks/notification.js +94 -0
- package/data/shared/framework/hooks/quality-gate.js +109 -0
- package/data/shared/framework/hooks/scout-block.js +77 -0
- package/data/shared/framework/hooks/telegram-notify.sh +77 -0
- package/data/shared/framework/protocols/error-recovery.md +80 -0
- package/data/shared/framework/protocols/orchestration-protocol.md +112 -0
- package/data/shared/framework/quality/review-protocol.md +76 -0
- package/data/shared/framework/quality/verification-protocol.md +66 -0
- package/data/shared/framework/rules/development-rules.md +75 -0
- package/data/shared/framework/skills/backend-development/SKILL.md +77 -0
- package/data/shared/framework/skills/backend-development/assets/sample-output.md +175 -0
- package/data/shared/framework/skills/backend-development/references/advanced-patterns.md +180 -0
- package/data/shared/framework/skills/backend-development/references/api-design-guide.md +160 -0
- package/data/shared/framework/skills/backend-development/references/architecture-patterns.md +183 -0
- package/data/shared/framework/skills/backend-development/references/observability-resilience.md +155 -0
- package/data/shared/framework/skills/backend-development/references/troubleshooting.md +199 -0
- package/data/shared/framework/skills/codebase-analysis/SKILL.md +72 -0
- package/data/shared/framework/skills/codebase-analysis/assets/sample-output.md +263 -0
- package/data/shared/framework/skills/codebase-analysis/references/analysis-techniques.md +241 -0
- package/data/shared/framework/skills/codebase-analysis/references/dependency-mapping.md +280 -0
- package/data/shared/framework/skills/codebase-analysis/references/tech-debt-assessment.md +208 -0
- package/data/shared/framework/skills/databases/SKILL.md +72 -0
- package/data/shared/framework/skills/databases/assets/sample-output.md +212 -0
- package/data/shared/framework/skills/databases/references/advanced-data-patterns.md +259 -0
- package/data/shared/framework/skills/databases/references/query-optimization.md +214 -0
- package/data/shared/framework/skills/databases/references/schema-design.md +159 -0
- package/data/shared/framework/skills/databases/references/troubleshooting.md +214 -0
- package/data/shared/framework/skills/debugging-investigation/SKILL.md +84 -0
- package/data/shared/framework/skills/debugging-investigation/assets/sample-output.md +314 -0
- package/data/shared/framework/skills/debugging-investigation/references/systematic-debugging.md +197 -0
- package/data/shared/framework/skills/debugging-investigation/references/tool-specific-guides.md +202 -0
- package/data/shared/framework/skills/debugging-investigation/references/troubleshooting-patterns.md +196 -0
- package/data/shared/framework/skills/frontend-development/SKILL.md +67 -0
- package/data/shared/framework/skills/frontend-development/assets/sample-output.md +110 -0
- package/data/shared/framework/skills/frontend-development/references/component-patterns.md +112 -0
- package/data/shared/framework/skills/frontend-development/references/performance-guide.md +169 -0
- package/data/shared/framework/skills/frontend-development/references/routing-forms-realtime.md +374 -0
- package/data/shared/framework/skills/frontend-development/references/ssr-rsc-patterns.md +284 -0
- package/data/shared/framework/skills/frontend-development/references/troubleshooting.md +154 -0
- package/data/shared/framework/skills/mobile-development/SKILL.md +67 -0
- package/data/shared/framework/skills/mobile-development/assets/sample-output.md +382 -0
- package/data/shared/framework/skills/mobile-development/references/mobile-patterns.md +681 -0
- package/data/shared/framework/skills/mobile-development/references/mobile-performance.md +524 -0
- package/data/shared/framework/skills/mobile-development/references/troubleshooting.md +158 -0
- package/data/shared/framework/skills/security-audit/SKILL.md +83 -0
- package/data/shared/framework/skills/security-audit/assets/sample-output.md +451 -0
- package/data/shared/framework/skills/security-audit/references/owasp-checklist.md +580 -0
- package/data/shared/framework/skills/security-audit/references/secure-coding-patterns.md +433 -0
- package/data/shared/framework/skills/security-audit/references/vulnerability-remediation.md +331 -0
- package/data/shared/framework/skills/ui-generation/SKILL.md +70 -0
- package/data/shared/framework/skills/ui-generation/assets/sample-output.md +139 -0
- package/data/shared/framework/skills/ui-generation/references/accessibility-responsive.md +127 -0
- package/data/shared/framework/skills/ui-generation/references/compound-components.md +252 -0
- package/data/shared/framework/skills/ui-generation/references/generation-patterns.md +110 -0
- package/data/shared/framework/skills/ui-generation/references/storybook-design-system.md +278 -0
- package/data/shared/framework/skills/ui-generation/references/troubleshooting.md +198 -0
- package/data/shared/framework/workflows/documentation-management.md +58 -0
- package/data/shared/framework/workflows/primary-workflow.md +88 -0
- package/dist/commands/activate.d.ts +3 -0
- package/dist/commands/activate.d.ts.map +1 -0
- package/dist/commands/activate.js +34 -0
- package/dist/commands/activate.js.map +1 -0
- package/dist/commands/bundle.d.ts +3 -0
- package/dist/commands/bundle.d.ts.map +1 -0
- package/dist/commands/bundle.js +64 -0
- package/dist/commands/bundle.js.map +1 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +99 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +37 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +30 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +35 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +68 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/download/cache.d.ts +3 -0
- package/dist/download/cache.d.ts.map +1 -0
- package/dist/download/cache.js +18 -0
- package/dist/download/cache.js.map +1 -0
- package/dist/download/client.d.ts +2 -0
- package/dist/download/client.d.ts.map +1 -0
- package/dist/download/client.js +58 -0
- package/dist/download/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/file-copier.d.ts +6 -0
- package/dist/installer/file-copier.d.ts.map +1 -0
- package/dist/installer/file-copier.js +32 -0
- package/dist/installer/file-copier.js.map +1 -0
- package/dist/installer/plugin-installer.d.ts +12 -0
- package/dist/installer/plugin-installer.d.ts.map +1 -0
- package/dist/installer/plugin-installer.js +33 -0
- package/dist/installer/plugin-installer.js.map +1 -0
- package/dist/installer/template-installer.d.ts +12 -0
- package/dist/installer/template-installer.d.ts.map +1 -0
- package/dist/installer/template-installer.js +45 -0
- package/dist/installer/template-installer.js.map +1 -0
- package/dist/license/crypto.d.ts +16 -0
- package/dist/license/crypto.d.ts.map +1 -0
- package/dist/license/crypto.js +50 -0
- package/dist/license/crypto.js.map +1 -0
- package/dist/license/license-store.d.ts +19 -0
- package/dist/license/license-store.d.ts.map +1 -0
- package/dist/license/license-store.js +99 -0
- package/dist/license/license-store.js.map +1 -0
- package/dist/license/validator.d.ts +32 -0
- package/dist/license/validator.d.ts.map +1 -0
- package/dist/license/validator.js +81 -0
- package/dist/license/validator.js.map +1 -0
- package/dist/registry/loader.d.ts +30 -0
- package/dist/registry/loader.d.ts.map +1 -0
- package/dist/registry/loader.js +22 -0
- package/dist/registry/loader.js.map +1 -0
- package/dist/registry/search-engine.d.ts +9 -0
- package/dist/registry/search-engine.d.ts.map +1 -0
- package/dist/registry/search-engine.js +30 -0
- package/dist/registry/search-engine.js.map +1 -0
- package/dist/utils/config.d.ts +14 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +28 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +22 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +20 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +79 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +54 -0
package/data/free-skills/devflow-agile/plugin/skills/developer/references/clean-architecture.md
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# Clean Architecture Reference
|
|
2
|
+
|
|
3
|
+
## SOLID Principles with Real Code Examples
|
|
4
|
+
|
|
5
|
+
### Single Responsibility Principle (SRP)
|
|
6
|
+
|
|
7
|
+
Every module should have one — and only one — reason to change.
|
|
8
|
+
|
|
9
|
+
**Bad** — One class handles validation, persistence, and notification:
|
|
10
|
+
```typescript
|
|
11
|
+
class OrderService {
|
|
12
|
+
async createOrder(data: CreateOrderDto) {
|
|
13
|
+
// Validation
|
|
14
|
+
if (!data.items?.length) throw new Error('No items');
|
|
15
|
+
if (data.total < 0) throw new Error('Invalid total');
|
|
16
|
+
|
|
17
|
+
// Persistence
|
|
18
|
+
const order = await this.db.orders.create({ data });
|
|
19
|
+
|
|
20
|
+
// Notification
|
|
21
|
+
await this.mailer.send(order.userEmail, 'Order created', orderTemplate(order));
|
|
22
|
+
|
|
23
|
+
return order;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Good** — Each concern is a separate class:
|
|
29
|
+
```typescript
|
|
30
|
+
class OrderValidator {
|
|
31
|
+
validate(data: CreateOrderDto): void {
|
|
32
|
+
if (!data.items?.length) throw new ValidationError('No items');
|
|
33
|
+
if (data.total < 0) throw new ValidationError('Invalid total');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class OrderRepository {
|
|
38
|
+
async create(data: CreateOrderDto): Promise<Order> {
|
|
39
|
+
return this.db.orders.create({ data });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class OrderNotifier {
|
|
44
|
+
async notifyCreated(order: Order): Promise<void> {
|
|
45
|
+
await this.mailer.send(order.userEmail, 'Order created', orderTemplate(order));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class CreateOrderUseCase {
|
|
50
|
+
constructor(
|
|
51
|
+
private validator: OrderValidator,
|
|
52
|
+
private repo: OrderRepository,
|
|
53
|
+
private notifier: OrderNotifier,
|
|
54
|
+
) {}
|
|
55
|
+
|
|
56
|
+
async execute(data: CreateOrderDto): Promise<Order> {
|
|
57
|
+
this.validator.validate(data);
|
|
58
|
+
const order = await this.repo.create(data);
|
|
59
|
+
await this.notifier.notifyCreated(order);
|
|
60
|
+
return order;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Open/Closed Principle (OCP)
|
|
66
|
+
|
|
67
|
+
Open for extension, closed for modification.
|
|
68
|
+
|
|
69
|
+
**Bad** — Adding a new export format requires modifying existing code:
|
|
70
|
+
```python
|
|
71
|
+
class ReportExporter:
|
|
72
|
+
def export(self, report: Report, format: str) -> bytes:
|
|
73
|
+
if format == "pdf":
|
|
74
|
+
return self._to_pdf(report)
|
|
75
|
+
elif format == "csv":
|
|
76
|
+
return self._to_csv(report)
|
|
77
|
+
# Every new format = another elif
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Good** — New formats are added by creating new classes:
|
|
81
|
+
```python
|
|
82
|
+
class ReportExporter(Protocol):
|
|
83
|
+
def export(self, report: Report) -> bytes: ...
|
|
84
|
+
|
|
85
|
+
class PdfExporter:
|
|
86
|
+
def export(self, report: Report) -> bytes:
|
|
87
|
+
# PDF generation logic
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
class CsvExporter:
|
|
91
|
+
def export(self, report: Report) -> bytes:
|
|
92
|
+
# CSV generation logic
|
|
93
|
+
...
|
|
94
|
+
|
|
95
|
+
# Registry pattern — no modification needed when adding new exporters
|
|
96
|
+
EXPORTERS: dict[str, ReportExporter] = {
|
|
97
|
+
"pdf": PdfExporter(),
|
|
98
|
+
"csv": CsvExporter(),
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Liskov Substitution Principle (LSP)
|
|
103
|
+
|
|
104
|
+
Subtypes must be substitutable for their base types without breaking behavior.
|
|
105
|
+
|
|
106
|
+
**Bad** — `ReadOnlyRepository` breaks the contract of `Repository`:
|
|
107
|
+
```typescript
|
|
108
|
+
class Repository<T> {
|
|
109
|
+
async findById(id: string): Promise<T> { /* ... */ }
|
|
110
|
+
async save(entity: T): Promise<void> { /* ... */ }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
class ReadOnlyRepository<T> extends Repository<T> {
|
|
114
|
+
async save(entity: T): Promise<void> {
|
|
115
|
+
throw new Error('Cannot save to read-only repository'); // BREAKS LSP
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Good** — Separate interfaces for read and write:
|
|
121
|
+
```typescript
|
|
122
|
+
interface ReadRepository<T> {
|
|
123
|
+
findById(id: string): Promise<T>;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
interface WriteRepository<T> {
|
|
127
|
+
save(entity: T): Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface Repository<T> extends ReadRepository<T>, WriteRepository<T> {}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Interface Segregation Principle (ISP)
|
|
134
|
+
|
|
135
|
+
Clients should not be forced to depend on interfaces they do not use.
|
|
136
|
+
|
|
137
|
+
**Bad** — One fat interface forces all implementations to handle everything:
|
|
138
|
+
```typescript
|
|
139
|
+
interface Worker {
|
|
140
|
+
writeCode(): void;
|
|
141
|
+
reviewCode(): void;
|
|
142
|
+
attendMeeting(): void;
|
|
143
|
+
manageServer(): void;
|
|
144
|
+
designUI(): void;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Good** — Focused interfaces that clients opt into:
|
|
149
|
+
```typescript
|
|
150
|
+
interface Coder { writeCode(): void; }
|
|
151
|
+
interface Reviewer { reviewCode(): void; }
|
|
152
|
+
interface MeetingAttendee { attendMeeting(): void; }
|
|
153
|
+
|
|
154
|
+
class Developer implements Coder, Reviewer, MeetingAttendee {
|
|
155
|
+
writeCode() { /* ... */ }
|
|
156
|
+
reviewCode() { /* ... */ }
|
|
157
|
+
attendMeeting() { /* ... */ }
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Dependency Inversion: Interface-Based Design
|
|
162
|
+
|
|
163
|
+
### Dependency Injection Pattern
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// 1. Define the port (interface)
|
|
167
|
+
interface PaymentGateway {
|
|
168
|
+
charge(amount: number, currency: string): Promise<PaymentResult>;
|
|
169
|
+
refund(transactionId: string): Promise<RefundResult>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 2. Implement the adapter
|
|
173
|
+
class StripeGateway implements PaymentGateway {
|
|
174
|
+
async charge(amount: number, currency: string): Promise<PaymentResult> {
|
|
175
|
+
return stripe.paymentIntents.create({ amount, currency });
|
|
176
|
+
}
|
|
177
|
+
async refund(transactionId: string): Promise<RefundResult> {
|
|
178
|
+
return stripe.refunds.create({ payment_intent: transactionId });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 3. Inject via constructor
|
|
183
|
+
class CheckoutService {
|
|
184
|
+
constructor(private gateway: PaymentGateway) {} // depends on interface
|
|
185
|
+
|
|
186
|
+
async checkout(cart: Cart): Promise<Order> {
|
|
187
|
+
const payment = await this.gateway.charge(cart.total, cart.currency);
|
|
188
|
+
// ...
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 4. Wire up in composition root (main / DI container)
|
|
193
|
+
const gateway = new StripeGateway();
|
|
194
|
+
const checkoutService = new CheckoutService(gateway);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Benefits**: Swap StripeGateway for MockGateway in tests. Replace Stripe with another provider without changing CheckoutService.
|
|
198
|
+
|
|
199
|
+
## Clean Architecture Layers
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
┌─────────────────────────────────────────────┐
|
|
203
|
+
│ Frameworks & Drivers │ Express, Prisma, React, AWS SDK
|
|
204
|
+
│ (outermost — depends on everything below) │
|
|
205
|
+
├─────────────────────────────────────────────┤
|
|
206
|
+
│ Interface Adapters │ Controllers, Presenters, Gateways
|
|
207
|
+
│ (converts data between use cases and │ Repository implementations
|
|
208
|
+
│ external formats) │
|
|
209
|
+
├─────────────────────────────────────────────┤
|
|
210
|
+
│ Use Cases │ Application-specific business rules
|
|
211
|
+
│ (orchestrates entities, enforces workflow) │ CreateOrder, ProcessPayment
|
|
212
|
+
├─────────────────────────────────────────────┤
|
|
213
|
+
│ Entities │ Enterprise-wide business rules
|
|
214
|
+
│ (innermost — depends on nothing) │ Order, User, Invoice
|
|
215
|
+
└─────────────────────────────────────────────┘
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Dependency Rule
|
|
219
|
+
Dependencies always point **inward**. Inner layers never import from outer layers. Outer layers depend on inner layers through interfaces.
|
|
220
|
+
|
|
221
|
+
### Practical Folder Structure
|
|
222
|
+
```
|
|
223
|
+
src/
|
|
224
|
+
├── domain/ # Entities + value objects (no external dependencies)
|
|
225
|
+
│ ├── order.ts
|
|
226
|
+
│ └── user.ts
|
|
227
|
+
├── application/ # Use cases + port interfaces
|
|
228
|
+
│ ├── ports/
|
|
229
|
+
│ │ ├── order-repository.port.ts
|
|
230
|
+
│ │ └── payment-gateway.port.ts
|
|
231
|
+
│ └── use-cases/
|
|
232
|
+
│ ├── create-order.use-case.ts
|
|
233
|
+
│ └── process-payment.use-case.ts
|
|
234
|
+
├── infrastructure/ # Adapter implementations
|
|
235
|
+
│ ├── database/
|
|
236
|
+
│ │ └── prisma-order-repository.ts
|
|
237
|
+
│ └── payment/
|
|
238
|
+
│ └── stripe-gateway.ts
|
|
239
|
+
└── presentation/ # Controllers, routes, CLI
|
|
240
|
+
├── http/
|
|
241
|
+
│ └── order.controller.ts
|
|
242
|
+
└── cli/
|
|
243
|
+
└── seed.command.ts
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Tech Debt Management
|
|
247
|
+
|
|
248
|
+
### Identification
|
|
249
|
+
|
|
250
|
+
| Type | Description | Example |
|
|
251
|
+
|------|-------------|---------|
|
|
252
|
+
| **Deliberate** | Conscious shortcut taken with a plan to fix | "Ship without caching, add in next sprint" |
|
|
253
|
+
| **Accidental** | Discovered after the fact | "This module has circular dependencies we didn't notice" |
|
|
254
|
+
| **Bit rot** | Gradual degradation over time | Outdated dependencies, deprecated API usage |
|
|
255
|
+
| **Design debt** | Architecture that no longer fits current needs | Monolith that should be split, wrong database choice |
|
|
256
|
+
|
|
257
|
+
### Classification Matrix
|
|
258
|
+
|
|
259
|
+
| | Low Effort to Fix | High Effort to Fix |
|
|
260
|
+
|---|---|---|
|
|
261
|
+
| **High Impact** | Fix now (current sprint) | Plan and schedule (next 1-2 sprints) |
|
|
262
|
+
| **Low Impact** | Fix opportunistically (when touching that code) | Track but deprioritize (backlog) |
|
|
263
|
+
|
|
264
|
+
### Payoff Strategies
|
|
265
|
+
- **Boy Scout Rule**: Leave code cleaner than you found it — small improvements on every PR
|
|
266
|
+
- **Tech debt budget**: Allocate 15-20% of sprint capacity to debt reduction
|
|
267
|
+
- **Quarterly cleanup sprint**: One sprint per quarter focused entirely on tech debt
|
|
268
|
+
- **Debt-driven refactoring**: When a feature is blocked or slowed by debt, fix the debt first
|
|
269
|
+
|
|
270
|
+
## Refactoring Patterns
|
|
271
|
+
|
|
272
|
+
### Extract Method
|
|
273
|
+
**When**: A block of code inside a function does a distinct thing (often preceded by a comment).
|
|
274
|
+
```typescript
|
|
275
|
+
// Before
|
|
276
|
+
function processOrder(order: Order) {
|
|
277
|
+
// validate inventory
|
|
278
|
+
for (const item of order.items) {
|
|
279
|
+
const stock = await getStock(item.productId);
|
|
280
|
+
if (stock < item.quantity) throw new InsufficientStockError(item.productId);
|
|
281
|
+
}
|
|
282
|
+
// ... rest of processing
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// After
|
|
286
|
+
function processOrder(order: Order) {
|
|
287
|
+
await validateInventory(order.items);
|
|
288
|
+
// ... rest of processing
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function validateInventory(items: OrderItem[]) {
|
|
292
|
+
for (const item of items) {
|
|
293
|
+
const stock = await getStock(item.productId);
|
|
294
|
+
if (stock < item.quantity) throw new InsufficientStockError(item.productId);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Replace Conditional with Polymorphism
|
|
300
|
+
**When**: A switch/if-else chain selects behavior based on type.
|
|
301
|
+
```typescript
|
|
302
|
+
// Before
|
|
303
|
+
function calculateShipping(type: string, weight: number): number {
|
|
304
|
+
if (type === 'standard') return weight * 0.5;
|
|
305
|
+
if (type === 'express') return weight * 1.5 + 10;
|
|
306
|
+
if (type === 'overnight') return weight * 3 + 25;
|
|
307
|
+
throw new Error(`Unknown type: ${type}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// After
|
|
311
|
+
interface ShippingCalculator {
|
|
312
|
+
calculate(weight: number): number;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
class StandardShipping implements ShippingCalculator {
|
|
316
|
+
calculate(weight: number) { return weight * 0.5; }
|
|
317
|
+
}
|
|
318
|
+
class ExpressShipping implements ShippingCalculator {
|
|
319
|
+
calculate(weight: number) { return weight * 1.5 + 10; }
|
|
320
|
+
}
|
|
321
|
+
class OvernightShipping implements ShippingCalculator {
|
|
322
|
+
calculate(weight: number) { return weight * 3 + 25; }
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Introduce Parameter Object
|
|
327
|
+
**When**: A function takes 4+ related parameters.
|
|
328
|
+
```typescript
|
|
329
|
+
// Before
|
|
330
|
+
function searchProducts(query: string, minPrice: number, maxPrice: number,
|
|
331
|
+
category: string, sortBy: string, page: number, limit: number) { /* ... */ }
|
|
332
|
+
|
|
333
|
+
// After
|
|
334
|
+
interface ProductSearchCriteria {
|
|
335
|
+
query: string;
|
|
336
|
+
priceRange: { min: number; max: number };
|
|
337
|
+
category: string;
|
|
338
|
+
sort: { field: string; direction: 'asc' | 'desc' };
|
|
339
|
+
pagination: { page: number; limit: number };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function searchProducts(criteria: ProductSearchCriteria) { /* ... */ }
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Code Review from Developer Perspective
|
|
346
|
+
|
|
347
|
+
### What to Look For (Priority Order)
|
|
348
|
+
|
|
349
|
+
1. **Correctness**: Does it do what the ticket says? Are edge cases handled?
|
|
350
|
+
2. **Security**: SQL injection, XSS, auth bypass, secrets in code?
|
|
351
|
+
3. **Performance**: N+1 queries, unbounded loops, missing indexes, memory leaks?
|
|
352
|
+
4. **Maintainability**: Can the next developer understand this in 6 months?
|
|
353
|
+
5. **Test coverage**: Are the important paths tested? Are tests meaningful (not just line coverage)?
|
|
354
|
+
6. **Style**: Consistent with codebase conventions? (Lowest priority — automate with linters)
|
|
355
|
+
|
|
356
|
+
### How to Give Constructive Feedback
|
|
357
|
+
- **Be specific**: "This query runs N+1 for each order item" not "This is slow"
|
|
358
|
+
- **Suggest alternatives**: "Consider using `Promise.all()` here to parallelize" not just "Fix this"
|
|
359
|
+
- **Distinguish must-fix from nice-to-have**: Use labels like `[blocking]` vs `[nit]` vs `[suggestion]`
|
|
360
|
+
- **Ask questions instead of commanding**: "Would it be simpler to extract this into a helper?" vs "Extract this"
|
|
361
|
+
- **Praise good code**: "Nice use of the builder pattern here" — positive feedback reinforces good practices
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Clean Code Guide
|
|
2
|
+
|
|
3
|
+
## SOLID Principles with Practical Examples
|
|
4
|
+
|
|
5
|
+
### Single Responsibility Principle (SRP)
|
|
6
|
+
A class or function should have **one reason to change**.
|
|
7
|
+
|
|
8
|
+
**Bad:**
|
|
9
|
+
```typescript
|
|
10
|
+
class UserService {
|
|
11
|
+
createUser(data: CreateUserDto) { /* creates user in DB */ }
|
|
12
|
+
sendWelcomeEmail(user: User) { /* sends email */ }
|
|
13
|
+
generateReport(users: User[]) { /* creates CSV report */ }
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Good:**
|
|
18
|
+
```typescript
|
|
19
|
+
class UserService {
|
|
20
|
+
createUser(data: CreateUserDto): User { /* creates user in DB */ }
|
|
21
|
+
}
|
|
22
|
+
class EmailService {
|
|
23
|
+
sendWelcomeEmail(user: User) { /* sends email */ }
|
|
24
|
+
}
|
|
25
|
+
class UserReportService {
|
|
26
|
+
generateReport(users: User[]): Buffer { /* creates CSV report */ }
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Open/Closed Principle (OCP)
|
|
31
|
+
Open for extension, closed for modification. Use abstractions to add behavior.
|
|
32
|
+
|
|
33
|
+
**Bad** — Adding a new payment method requires modifying existing code:
|
|
34
|
+
```typescript
|
|
35
|
+
function processPayment(method: string, amount: number) {
|
|
36
|
+
if (method === 'stripe') { /* stripe logic */ }
|
|
37
|
+
else if (method === 'paypal') { /* paypal logic */ }
|
|
38
|
+
// Every new method = another else-if
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Good** — New payment methods are added without touching existing code:
|
|
43
|
+
```typescript
|
|
44
|
+
interface PaymentProcessor {
|
|
45
|
+
process(amount: number): PaymentResult;
|
|
46
|
+
}
|
|
47
|
+
class StripeProcessor implements PaymentProcessor { /* ... */ }
|
|
48
|
+
class PaypalProcessor implements PaymentProcessor { /* ... */ }
|
|
49
|
+
// Adding BankTransferProcessor requires zero changes to existing code
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Dependency Inversion Principle (DIP)
|
|
53
|
+
Depend on abstractions, not concretions. High-level modules should not depend on low-level modules.
|
|
54
|
+
|
|
55
|
+
**Bad:**
|
|
56
|
+
```typescript
|
|
57
|
+
class OrderService {
|
|
58
|
+
private db = new PostgresDatabase(); // tightly coupled to Postgres
|
|
59
|
+
private mailer = new SendGridMailer(); // tightly coupled to SendGrid
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Good:**
|
|
64
|
+
```typescript
|
|
65
|
+
class OrderService {
|
|
66
|
+
constructor(
|
|
67
|
+
private db: DatabasePort, // depends on interface
|
|
68
|
+
private mailer: MailerPort, // depends on interface
|
|
69
|
+
) {}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## DRY, KISS, YAGNI Application
|
|
74
|
+
|
|
75
|
+
| Principle | Meaning | Practical Rule |
|
|
76
|
+
|-----------|---------|---------------|
|
|
77
|
+
| **DRY** | Don't Repeat Yourself | If you copy-paste code a **third time**, extract it. Twice is OK — premature abstraction is worse than duplication |
|
|
78
|
+
| **KISS** | Keep It Simple | Choose the simplest solution that works. A 10-line if/else is often better than a design pattern nobody understands |
|
|
79
|
+
| **YAGNI** | You Aren't Gonna Need It | Do not build features, abstractions, or infrastructure "just in case." Build what the current ticket requires |
|
|
80
|
+
|
|
81
|
+
### DRY Anti-Pattern: Wrong Abstraction
|
|
82
|
+
Sharing code between two callers that happen to look similar today but have different reasons to change leads to **coupling that hurts more than the duplication**. When in doubt, duplicate for now and extract later when the pattern is clear.
|
|
83
|
+
|
|
84
|
+
## Function Design
|
|
85
|
+
|
|
86
|
+
### Rules
|
|
87
|
+
| Rule | Guideline |
|
|
88
|
+
|------|-----------|
|
|
89
|
+
| Length | Aim for under 20 lines. Hard limit at 50 lines — extract subfunctions beyond that |
|
|
90
|
+
| Parameters | 3 or fewer. If more, group into an options object / DTO |
|
|
91
|
+
| Single abstraction level | A function should not mix high-level orchestration with low-level implementation details |
|
|
92
|
+
| Return early | Validate inputs at the top, return/throw early. Avoid deep nesting |
|
|
93
|
+
| Side effects | Minimize. If a function has side effects, make them obvious from the name (`saveUser`, not `getUser` that also saves) |
|
|
94
|
+
|
|
95
|
+
### Example: Return Early Pattern
|
|
96
|
+
**Bad** — Deeply nested:
|
|
97
|
+
```typescript
|
|
98
|
+
function processOrder(order: Order) {
|
|
99
|
+
if (order) {
|
|
100
|
+
if (order.items.length > 0) {
|
|
101
|
+
if (order.payment) {
|
|
102
|
+
// actual logic buried 3 levels deep
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Good** — Guard clauses:
|
|
110
|
+
```typescript
|
|
111
|
+
function processOrder(order: Order) {
|
|
112
|
+
if (!order) throw new InvalidOrderError('Order is required');
|
|
113
|
+
if (order.items.length === 0) throw new InvalidOrderError('Order has no items');
|
|
114
|
+
if (!order.payment) throw new InvalidOrderError('Payment info missing');
|
|
115
|
+
|
|
116
|
+
// actual logic at top level, easy to read
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Error Handling Patterns
|
|
121
|
+
|
|
122
|
+
### Rules
|
|
123
|
+
1. **Never swallow exceptions** — `catch (e) {}` is a bug waiting to happen
|
|
124
|
+
2. **Catch at the right level** — catch where you can actually handle or translate the error
|
|
125
|
+
3. **Use custom error classes** for business errors (not generic Error/Exception)
|
|
126
|
+
4. **Include context** — error messages should help debugging without looking at code
|
|
127
|
+
|
|
128
|
+
### Error Handling Strategy
|
|
129
|
+
| Layer | Approach |
|
|
130
|
+
|-------|---------|
|
|
131
|
+
| Controller/Route | Catch and translate to HTTP status + user-friendly message |
|
|
132
|
+
| Service | Throw domain-specific errors (`InsufficientBalanceError`, `UserNotFoundError`) |
|
|
133
|
+
| Repository/DB | Translate database errors to domain errors (constraint violation → `DuplicateEmailError`) |
|
|
134
|
+
| External API | Wrap and translate third-party errors, add retry for transient failures |
|
|
135
|
+
|
|
136
|
+
### Example
|
|
137
|
+
```typescript
|
|
138
|
+
// BAD
|
|
139
|
+
try {
|
|
140
|
+
await paymentGateway.charge(amount);
|
|
141
|
+
} catch (e) {
|
|
142
|
+
console.log('error'); // No context, swallowed
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// GOOD
|
|
146
|
+
try {
|
|
147
|
+
await paymentGateway.charge(amount);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
logger.error('Payment charge failed', {
|
|
150
|
+
orderId: order.id,
|
|
151
|
+
amount,
|
|
152
|
+
gateway: 'stripe',
|
|
153
|
+
error: error.message,
|
|
154
|
+
});
|
|
155
|
+
throw new PaymentFailedError(order.id, error);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Logging Best Practices
|
|
160
|
+
|
|
161
|
+
| Rule | Guideline |
|
|
162
|
+
|------|-----------|
|
|
163
|
+
| **Log context, not noise** | Include: requestId, userId, orderId. Skip: "entering function X" |
|
|
164
|
+
| **Structured logging** | Use JSON format with consistent fields, not free-text strings |
|
|
165
|
+
| **Log levels matter** | ERROR: needs attention. WARN: unusual but handled. INFO: business events. DEBUG: development only |
|
|
166
|
+
| **Never log secrets** | Passwords, tokens, full credit card numbers, PII must never appear in logs |
|
|
167
|
+
| **Log at boundaries** | Log incoming requests, outgoing API calls, and state transitions — not every line |
|
|
168
|
+
|
|
169
|
+
## Code Documentation Standards
|
|
170
|
+
|
|
171
|
+
### When to Comment
|
|
172
|
+
- **Why**, not what — the code shows what, comments explain why
|
|
173
|
+
- **Business rules** — "Discount applies only to orders over $50 per client contract section 3.2"
|
|
174
|
+
- **Workarounds** — "Using setTimeout because library X has a race condition (see issue #234)"
|
|
175
|
+
- **Public APIs** — JSDoc/docstring for every public function with params and return value
|
|
176
|
+
|
|
177
|
+
### When NOT to Comment
|
|
178
|
+
- `// increment counter` → `counter++` — the code is self-explanatory
|
|
179
|
+
- `// TODO: fix this later` without a ticket number — create a ticket or fix it now
|
|
180
|
+
- Commented-out code — delete it, git remembers
|
|
181
|
+
|
|
182
|
+
## Common Refactoring Techniques
|
|
183
|
+
|
|
184
|
+
| Technique | When to Use | How |
|
|
185
|
+
|-----------|-------------|-----|
|
|
186
|
+
| Extract Function | Block of code with a comment explaining what it does | Move the block to a named function — the name replaces the comment |
|
|
187
|
+
| Introduce Parameter Object | Function with 4+ parameters | Group related params into a DTO/interface |
|
|
188
|
+
| Replace Conditional with Polymorphism | Long if/else or switch on type | Create subclasses or strategy pattern implementations |
|
|
189
|
+
| Decompose Conditional | Complex boolean expression | Extract to a named function: `isEligibleForDiscount()` |
|
|
190
|
+
| Replace Magic Number | `if (status === 3)` | Define a constant or enum: `if (status === OrderStatus.SHIPPED)` |
|
|
191
|
+
| Move to Caller/Callee | Responsibility in the wrong place | Move logic to where the data lives |
|
|
192
|
+
|
|
193
|
+
## Code Complexity Management
|
|
194
|
+
|
|
195
|
+
### Cyclomatic Complexity Targets
|
|
196
|
+
| Complexity | Assessment | Action |
|
|
197
|
+
|-----------|-----------|--------|
|
|
198
|
+
| 1–5 | Simple, easy to test | No action needed |
|
|
199
|
+
| 6–10 | Moderate, still manageable | Monitor, refactor if adding more branches |
|
|
200
|
+
| 11–20 | High, hard to test thoroughly | Refactor: extract functions, simplify conditions |
|
|
201
|
+
| 21+ | Very high, error-prone | Must refactor before adding any new logic |
|
|
202
|
+
|
|
203
|
+
### Reducing Complexity
|
|
204
|
+
1. **Extract early returns** — handle errors/edge cases upfront, reduce nesting
|
|
205
|
+
2. **Use lookup tables** instead of switch/case for data mapping
|
|
206
|
+
3. **Split decision from action** — one function decides, another executes
|
|
207
|
+
4. **Limit function scope** — if a function handles 3 concerns, split into 3 functions
|