dev-playbooks 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/LICENSE +21 -0
- package/README.md +463 -0
- package/bin/devbooks.js +986 -0
- package/package.json +41 -0
- package/skills/Skill-Development-Guide.md +249 -0
- package/skills/Skills-Usage-Guide.md +447 -0
- package/skills/_shared/context-detection-template.md +315 -0
- package/skills/_shared/mcp-enhancement-template.md +144 -0
- package/skills/_shared/references/universal-gating-protocol.md +114 -0
- package/skills/_template/config-discovery-template.md +126 -0
- package/skills/devbooks-brownfield-bootstrap/SKILL.md +168 -0
- package/skills/devbooks-brownfield-bootstrap/references/10-glossary-template.md +42 -0
- package/skills/devbooks-brownfield-bootstrap/references/brownfield-bootstrap-prompt.md +115 -0
- package/skills/devbooks-brownfield-bootstrap/references/brownfield-bootstrap.md +96 -0
- package/skills/devbooks-brownfield-bootstrap/references/code-navigation-strategy.md +203 -0
- package/skills/devbooks-brownfield-bootstrap/scripts/cod-update.sh +357 -0
- package/skills/devbooks-brownfield-bootstrap/templates/project-profile-template.md +172 -0
- package/skills/devbooks-c4-map/SKILL.md +151 -0
- package/skills/devbooks-c4-map/references/c4-architecture-map-prompt.md +33 -0
- package/skills/devbooks-c4-map/references/layered-constraint-checklist.md +185 -0
- package/skills/devbooks-code-review/SKILL.md +175 -0
- package/skills/devbooks-code-review/references/code-review-prompt.md +100 -0
- package/skills/devbooks-code-review/references/code-smell-cheatsheet.md +498 -0
- package/skills/devbooks-code-review/references/pr-template-and-guidelines.md +321 -0
- package/skills/devbooks-code-review/references/resource-management-review-checklist.md +311 -0
- package/skills/devbooks-coder/SKILL.md +219 -0
- package/skills/devbooks-coder/references/code-implementation-prompt.md +74 -0
- package/skills/devbooks-coder/references/coding-style-guidelines.md +351 -0
- package/skills/devbooks-coder/references/error-code-standard.md +463 -0
- package/skills/devbooks-coder/references/logging-standard.md +329 -0
- package/skills/devbooks-coder/references/low-risk-modification-techniques.md +275 -0
- package/skills/devbooks-delivery-workflow/SKILL.md +217 -0
- package/skills/devbooks-delivery-workflow/references/9-change-verification-traceability-template.md +133 -0
- package/skills/devbooks-delivery-workflow/references/delivery-acceptance-workflow.md +177 -0
- package/skills/devbooks-delivery-workflow/references/prototype-production-dual-track.md +169 -0
- package/skills/devbooks-delivery-workflow/scripts/ac-trace-check.sh +330 -0
- package/skills/devbooks-delivery-workflow/scripts/audit-scope.sh +262 -0
- package/skills/devbooks-delivery-workflow/scripts/change-check.sh +1039 -0
- package/skills/devbooks-delivery-workflow/scripts/change-codemod-scaffold.sh +135 -0
- package/skills/devbooks-delivery-workflow/scripts/change-evidence.sh +152 -0
- package/skills/devbooks-delivery-workflow/scripts/change-scaffold.sh +467 -0
- package/skills/devbooks-delivery-workflow/scripts/change-spec-delta-scaffold.sh +135 -0
- package/skills/devbooks-delivery-workflow/scripts/constitution-check.sh +237 -0
- package/skills/devbooks-delivery-workflow/scripts/env-match-check.sh +128 -0
- package/skills/devbooks-delivery-workflow/scripts/fitness-check.sh +365 -0
- package/skills/devbooks-delivery-workflow/scripts/guardrail-check.sh +516 -0
- package/skills/devbooks-delivery-workflow/scripts/handoff-check.sh +141 -0
- package/skills/devbooks-delivery-workflow/scripts/hygiene-check.sh +340 -0
- package/skills/devbooks-delivery-workflow/scripts/migrate-from-openspec.sh +385 -0
- package/skills/devbooks-delivery-workflow/scripts/migrate-to-v2-gates.sh +202 -0
- package/skills/devbooks-delivery-workflow/scripts/progress-dashboard.sh +319 -0
- package/skills/devbooks-delivery-workflow/scripts/prototype-promote.sh +341 -0
- package/skills/devbooks-delivery-workflow/scripts/spec-preview.sh +203 -0
- package/skills/devbooks-delivery-workflow/scripts/spec-promote.sh +118 -0
- package/skills/devbooks-delivery-workflow/scripts/spec-rollback.sh +124 -0
- package/skills/devbooks-delivery-workflow/scripts/spec-stage.sh +117 -0
- package/skills/devbooks-delivery-workflow/scripts/verify-all.sh +78 -0
- package/skills/devbooks-delivery-workflow/scripts/verify-npm-package.sh +123 -0
- package/skills/devbooks-delivery-workflow/scripts/verify-openspec-free.sh +81 -0
- package/skills/devbooks-delivery-workflow/scripts/verify-slash-commands.sh +146 -0
- package/skills/devbooks-delivery-workflow/templates/handoff.md +50 -0
- package/skills/devbooks-design-backport/SKILL.md +73 -0
- package/skills/devbooks-design-backport/references/design-backport-prompt.md +132 -0
- package/skills/devbooks-design-doc/SKILL.md +121 -0
- package/skills/devbooks-design-doc/references/design-doc-prompt.md +188 -0
- package/skills/devbooks-design-doc/references/microservice-design-checklist.md +149 -0
- package/skills/devbooks-design-doc/references/privacy-compliance-checklist.md +240 -0
- package/skills/devbooks-entropy-monitor/SKILL.md +188 -0
- package/skills/devbooks-entropy-monitor/references/entropy-metrics-methodology.md +218 -0
- package/skills/devbooks-entropy-monitor/scripts/entropy-measure.sh +449 -0
- package/skills/devbooks-entropy-monitor/scripts/entropy-report.sh +303 -0
- package/skills/devbooks-entropy-monitor/templates/thresholds.json +99 -0
- package/skills/devbooks-federation/SKILL.md +264 -0
- package/skills/devbooks-federation/scripts/federation-check.sh +144 -0
- package/skills/devbooks-federation/templates/federation.yaml +89 -0
- package/skills/devbooks-impact-analysis/SKILL.md +135 -0
- package/skills/devbooks-impact-analysis/references/impact-analysis-prompt.md +82 -0
- package/skills/devbooks-impact-analysis/scripts/graph-cache.sh +214 -0
- package/skills/devbooks-implementation-plan/SKILL.md +83 -0
- package/skills/devbooks-implementation-plan/references/implementation-plan-prompt.md +95 -0
- package/skills/devbooks-index-bootstrap/SKILL.md +240 -0
- package/skills/devbooks-proposal-author/SKILL.md +83 -0
- package/skills/devbooks-proposal-author/references/proposal-authoring-prompt.md +66 -0
- package/skills/devbooks-proposal-challenger/SKILL.md +86 -0
- package/skills/devbooks-proposal-challenger/references/ethics-and-compliance-checklist.md +176 -0
- package/skills/devbooks-proposal-challenger/references/proposal-challenge-prompt.md +57 -0
- package/skills/devbooks-proposal-debate-workflow/SKILL.md +78 -0
- package/skills/devbooks-proposal-debate-workflow/references/11-proposal-debate-template.md +35 -0
- package/skills/devbooks-proposal-debate-workflow/references/proposal-debate-workflow.md +24 -0
- package/skills/devbooks-proposal-debate-workflow/scripts/proposal-debate-check.sh +102 -0
- package/skills/devbooks-proposal-judge/SKILL.md +78 -0
- package/skills/devbooks-proposal-judge/references/proposal-judge-prompt.md +37 -0
- package/skills/devbooks-router/SKILL.md +346 -0
- package/skills/devbooks-spec-contract/SKILL.md +191 -0
- package/skills/devbooks-spec-contract/references/api-design-guide.md +349 -0
- package/skills/devbooks-spec-contract/references/contract-and-data-definition-prompt.md +85 -0
- package/skills/devbooks-spec-contract/references/implicit-change-detection-prompt.md +183 -0
- package/skills/devbooks-spec-contract/references/spec-change-prompt.md +63 -0
- package/skills/devbooks-spec-contract/scripts/implicit-change-detect.sh +378 -0
- package/skills/devbooks-spec-gardener/SKILL.md +73 -0
- package/skills/devbooks-spec-gardener/references/spec-gardener-prompt.md +41 -0
- package/skills/devbooks-test-owner/SKILL.md +173 -0
- package/skills/devbooks-test-owner/references/9-change-verification-traceability-template.md +133 -0
- package/skills/devbooks-test-owner/references/async-system-test-strategy.md +316 -0
- package/skills/devbooks-test-owner/references/decoupling-techniques-cheatsheet.md +269 -0
- package/skills/devbooks-test-owner/references/test-code-prompt.md +171 -0
- package/skills/devbooks-test-owner/references/test-driven-development.md +351 -0
- package/skills/devbooks-test-owner/references/test-layering-strategy.md +281 -0
- package/skills/devbooks-test-reviewer/SKILL.md +189 -0
- package/templates/.devbooks/config.yaml +88 -0
- package/templates/claude-commands/devbooks/apply.md +38 -0
- package/templates/claude-commands/devbooks/archive.md +33 -0
- package/templates/claude-commands/devbooks/backport.md +19 -0
- package/templates/claude-commands/devbooks/bootstrap.md +20 -0
- package/templates/claude-commands/devbooks/c4.md +20 -0
- package/templates/claude-commands/devbooks/challenger.md +19 -0
- package/templates/claude-commands/devbooks/code.md +20 -0
- package/templates/claude-commands/devbooks/debate.md +20 -0
- package/templates/claude-commands/devbooks/delivery.md +20 -0
- package/templates/claude-commands/devbooks/design.md +20 -0
- package/templates/claude-commands/devbooks/entropy.md +19 -0
- package/templates/claude-commands/devbooks/federation.md +19 -0
- package/templates/claude-commands/devbooks/gardener.md +19 -0
- package/templates/claude-commands/devbooks/impact.md +19 -0
- package/templates/claude-commands/devbooks/index.md +19 -0
- package/templates/claude-commands/devbooks/judge.md +19 -0
- package/templates/claude-commands/devbooks/plan.md +20 -0
- package/templates/claude-commands/devbooks/proposal.md +20 -0
- package/templates/claude-commands/devbooks/quick.md +43 -0
- package/templates/claude-commands/devbooks/review.md +20 -0
- package/templates/claude-commands/devbooks/router.md +19 -0
- package/templates/claude-commands/devbooks/spec.md +20 -0
- package/templates/claude-commands/devbooks/test-review.md +19 -0
- package/templates/claude-commands/devbooks/test.md +20 -0
- package/templates/dev-playbooks/changes/.gitkeep +1 -0
- package/templates/dev-playbooks/constitution.md +116 -0
- package/templates/dev-playbooks/project.md +96 -0
- package/templates/dev-playbooks/scripts/.gitkeep +1 -0
- package/templates/dev-playbooks/specs/_meta/anti-patterns/.gitkeep +2 -0
- package/templates/dev-playbooks/specs/_meta/glossary.md +48 -0
- package/templates/dev-playbooks/specs/_meta/project-profile.md +79 -0
- package/templates/dev-playbooks/specs/architecture/fitness-rules.md +95 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
# Core Code Smells Cheat Sheet (Top 8 + Module Cycles)
|
|
2
|
+
|
|
3
|
+
> Source: *Refactoring: Improving the Design of Existing Code (2nd Edition)* (debate-revised summary)
|
|
4
|
+
> This reduces the original 22 smells down to 8 high-frequency, high-impact smells for day-to-day reviews.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Quick Index
|
|
9
|
+
|
|
10
|
+
| # | Smell | One-line description | Severity |
|
|
11
|
+
|---|-------|----------------------|----------|
|
|
12
|
+
| 1 | Duplicated Code | The same logic appears in multiple places | Blocker |
|
|
13
|
+
| 2 | Long Method | A function is too long to understand easily | Blocker |
|
|
14
|
+
| 3 | Large Class | A class takes on too many responsibilities | Warning |
|
|
15
|
+
| 4 | Long Parameter List | Too many parameters make calls fragile | Blocker |
|
|
16
|
+
| 5 | Divergent Change | One class changes for multiple reasons | Warning |
|
|
17
|
+
| 6 | Shotgun Surgery | One change requires touching many places | Blocker |
|
|
18
|
+
| 7 | Feature Envy | A method depends too much on another class | Warning |
|
|
19
|
+
| 8 | Primitive Obsession | Domain concepts are modeled as primitives | Warning |
|
|
20
|
+
| 9 | **Module Cycle** | A→B→A blocks independent build/test | **Blocker** |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Duplicated Code
|
|
25
|
+
|
|
26
|
+
**Signals:**
|
|
27
|
+
- Similarity > 80% appears in 2+ places
|
|
28
|
+
- Copy/paste with only variable names changed
|
|
29
|
+
- Duplicate logic across sibling subclasses
|
|
30
|
+
|
|
31
|
+
**Why it matters:**
|
|
32
|
+
- Easy to miss one site when changing behavior
|
|
33
|
+
- Increases code size and reduces readability
|
|
34
|
+
- Violates DRY
|
|
35
|
+
|
|
36
|
+
**Refactorings:**
|
|
37
|
+
1. **Duplicates within one class** → Extract Method
|
|
38
|
+
2. **Duplicates across sibling subclasses** → Extract Method → Pull Up Method
|
|
39
|
+
3. **Duplicates across unrelated classes** → Extract Class (shared component)
|
|
40
|
+
|
|
41
|
+
**Example:**
|
|
42
|
+
```python
|
|
43
|
+
# Smell: duplicated validation logic
|
|
44
|
+
def create_user(email):
|
|
45
|
+
if not email or '@' not in email:
|
|
46
|
+
raise ValueError("Invalid email")
|
|
47
|
+
# ...
|
|
48
|
+
|
|
49
|
+
def update_email(email):
|
|
50
|
+
if not email or '@' not in email: # duplicated!
|
|
51
|
+
raise ValueError("Invalid email")
|
|
52
|
+
# ...
|
|
53
|
+
|
|
54
|
+
# Refactor: extract a helper
|
|
55
|
+
def validate_email(email):
|
|
56
|
+
if not email or '@' not in email:
|
|
57
|
+
raise ValueError("Invalid email")
|
|
58
|
+
|
|
59
|
+
def create_user(email):
|
|
60
|
+
validate_email(email)
|
|
61
|
+
# ...
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 2. Long Method
|
|
67
|
+
|
|
68
|
+
**Signals:**
|
|
69
|
+
- **P95 < 50 lines** (use as a discussion trigger, not a hard rule)
|
|
70
|
+
- You need to scroll to read the full function
|
|
71
|
+
- The function contains large comment blocks explaining “what this part does”
|
|
72
|
+
- Cyclomatic complexity > 10
|
|
73
|
+
|
|
74
|
+
**Why it matters:**
|
|
75
|
+
- Hard to understand the overall intent
|
|
76
|
+
- Hard to test (too many branches)
|
|
77
|
+
- Hard to reuse partial logic
|
|
78
|
+
|
|
79
|
+
**Refactorings:**
|
|
80
|
+
1. **Comments are a signal** → Extract Method (use the comment as the method name)
|
|
81
|
+
2. **Too many temporaries** → Replace Temp with Query
|
|
82
|
+
3. **Complex conditionals** → Decompose Conditional
|
|
83
|
+
|
|
84
|
+
**Example:**
|
|
85
|
+
```python
|
|
86
|
+
# Smell: long method
|
|
87
|
+
def process_order(order):
|
|
88
|
+
# validate order
|
|
89
|
+
if not order.items:
|
|
90
|
+
raise ValueError("Empty order")
|
|
91
|
+
if order.total < 0:
|
|
92
|
+
raise ValueError("Invalid total")
|
|
93
|
+
|
|
94
|
+
# calculate discount
|
|
95
|
+
discount = 0
|
|
96
|
+
if order.customer.is_vip:
|
|
97
|
+
discount = order.total * 0.1
|
|
98
|
+
elif order.total > 1000:
|
|
99
|
+
discount = order.total * 0.05
|
|
100
|
+
|
|
101
|
+
# update inventory
|
|
102
|
+
for item in order.items:
|
|
103
|
+
stock = get_stock(item.product_id)
|
|
104
|
+
stock.quantity -= item.quantity
|
|
105
|
+
save_stock(stock)
|
|
106
|
+
|
|
107
|
+
# ... 50 more lines
|
|
108
|
+
|
|
109
|
+
# Refactor: extract helpers
|
|
110
|
+
def process_order(order):
|
|
111
|
+
validate_order(order)
|
|
112
|
+
discount = calculate_discount(order)
|
|
113
|
+
update_inventory(order)
|
|
114
|
+
# ...
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 3. Large Class
|
|
120
|
+
|
|
121
|
+
**Signals:**
|
|
122
|
+
- **P95 < 500 lines**
|
|
123
|
+
- > 10 instance fields
|
|
124
|
+
- > 20 methods
|
|
125
|
+
- Multiple groups of fields with shared prefixes (e.g., `billing_*`, `shipping_*`)
|
|
126
|
+
|
|
127
|
+
**Why it matters:**
|
|
128
|
+
- Violates SRP (Single Responsibility Principle)
|
|
129
|
+
- Hard to test (too many dependencies)
|
|
130
|
+
- Change impact becomes unpredictable
|
|
131
|
+
|
|
132
|
+
**Refactorings:**
|
|
133
|
+
1. **Responsibilities can be separated** → Extract Class
|
|
134
|
+
2. **There are true subtypes** → Extract Subclass
|
|
135
|
+
3. **Consumers only need a subset** → Extract Interface
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 4. Long Parameter List
|
|
140
|
+
|
|
141
|
+
**Signals:**
|
|
142
|
+
- More than 5 parameters
|
|
143
|
+
- Call sites often mix up parameter order
|
|
144
|
+
- Multiple functions share the same parameter “bundle”
|
|
145
|
+
|
|
146
|
+
**Why it matters:**
|
|
147
|
+
- Easy to pass the wrong argument
|
|
148
|
+
- Hard to remember the signature
|
|
149
|
+
- The bundle is often a hidden domain concept
|
|
150
|
+
|
|
151
|
+
**Refactorings:**
|
|
152
|
+
1. **Arguments can be obtained from an object** → Preserve Whole Object
|
|
153
|
+
2. **Arguments always travel together** → Introduce Parameter Object
|
|
154
|
+
|
|
155
|
+
**Example:**
|
|
156
|
+
```python
|
|
157
|
+
# Smell: too many parameters
|
|
158
|
+
def create_address(street, city, state, zip_code, country, apt_number):
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
# Refactor: introduce a parameter object
|
|
162
|
+
@dataclass
|
|
163
|
+
class Address:
|
|
164
|
+
street: str
|
|
165
|
+
city: str
|
|
166
|
+
state: str
|
|
167
|
+
zip_code: str
|
|
168
|
+
country: str
|
|
169
|
+
apt_number: str = None
|
|
170
|
+
|
|
171
|
+
def create_address(address: Address):
|
|
172
|
+
pass
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 5. Divergent Change
|
|
178
|
+
|
|
179
|
+
**Signals:**
|
|
180
|
+
- Database changes require editing this class
|
|
181
|
+
- UI changes also require editing this class
|
|
182
|
+
- Business rule changes require editing this class too
|
|
183
|
+
- “Every requirement change touches this file”
|
|
184
|
+
|
|
185
|
+
**Why it matters:**
|
|
186
|
+
- One class carries multiple change axes
|
|
187
|
+
- Unrelated edits interfere with each other
|
|
188
|
+
- Hard to test one dimension in isolation
|
|
189
|
+
|
|
190
|
+
**Refactorings:**
|
|
191
|
+
- Extract Class (split by reason-to-change)
|
|
192
|
+
|
|
193
|
+
**Compared to Shotgun Surgery:**
|
|
194
|
+
- Divergent Change: one class responds to many changes
|
|
195
|
+
- Shotgun Surgery: one change requires touching many classes
|
|
196
|
+
- They are dual problems with opposite fixes
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 6. Shotgun Surgery
|
|
201
|
+
|
|
202
|
+
**Signals:**
|
|
203
|
+
- One requirement forces edits in 3+ classes/files
|
|
204
|
+
- “Fixing one place means changing lots of other places”
|
|
205
|
+
- Easy to miss one spot and ship a bug
|
|
206
|
+
|
|
207
|
+
**Why it matters:**
|
|
208
|
+
- Changes are easy to overlook
|
|
209
|
+
- Logic is scattered and hard to reason about
|
|
210
|
+
- Test coverage becomes expensive
|
|
211
|
+
|
|
212
|
+
**Refactorings:**
|
|
213
|
+
1. **Logic should be centralized** → Move Method / Move Field
|
|
214
|
+
2. **Over-fragmented code** → Inline Class (merge first, then re-split intentionally)
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 7. Feature Envy
|
|
219
|
+
|
|
220
|
+
**Signals:**
|
|
221
|
+
- A method calls other classes more than it uses its own class
|
|
222
|
+
- A method reads many fields from another class
|
|
223
|
+
- “This method feels like it belongs elsewhere”
|
|
224
|
+
|
|
225
|
+
**Why it matters:**
|
|
226
|
+
- Violates “data and behavior belong together”
|
|
227
|
+
- Increases coupling
|
|
228
|
+
- Blurs responsibility boundaries
|
|
229
|
+
|
|
230
|
+
**Refactorings:**
|
|
231
|
+
- Move Method (move closer to the data it uses)
|
|
232
|
+
|
|
233
|
+
**Common exceptions (not Feature Envy):**
|
|
234
|
+
- Strategy pattern (strategy accessing context)
|
|
235
|
+
- Visitor pattern (visitor accessing elements)
|
|
236
|
+
- If this is intentional, document it as a pattern choice
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 8. Primitive Obsession
|
|
241
|
+
|
|
242
|
+
**Signals:**
|
|
243
|
+
- `string` used for phone numbers, emails, money
|
|
244
|
+
- `int` used for status codes or type codes
|
|
245
|
+
- Business rules are duplicated across the codebase (e.g., email validation)
|
|
246
|
+
|
|
247
|
+
**Why it matters:**
|
|
248
|
+
- Poor type safety (a `string` can be anything)
|
|
249
|
+
- Business rules can’t be centralized
|
|
250
|
+
- Domain language drifts away from the project glossary
|
|
251
|
+
|
|
252
|
+
**Refactorings:**
|
|
253
|
+
- Replace Data Value with Object
|
|
254
|
+
- Replace Type Code with Class/Subclass
|
|
255
|
+
|
|
256
|
+
**Scope guidance (debate-revised):**
|
|
257
|
+
- **Must encapsulate:** domain concepts (Money, Email, UserId, PhoneNumber)
|
|
258
|
+
- **Optional encapsulation:** technical types (coordinates, colors, simple configs)
|
|
259
|
+
|
|
260
|
+
**Example:**
|
|
261
|
+
```python
|
|
262
|
+
# Smell: primitives used for domain concepts
|
|
263
|
+
def transfer(from_account: str, to_account: str, amount: float):
|
|
264
|
+
pass # amount can be negative? which currency?
|
|
265
|
+
|
|
266
|
+
# Refactor: encapsulate into a value object
|
|
267
|
+
@dataclass(frozen=True)
|
|
268
|
+
class Money:
|
|
269
|
+
amount: Decimal
|
|
270
|
+
currency: str
|
|
271
|
+
|
|
272
|
+
def __post_init__(self):
|
|
273
|
+
if self.amount < 0:
|
|
274
|
+
raise ValueError("Amount cannot be negative")
|
|
275
|
+
|
|
276
|
+
def transfer(from_account: AccountId, to_account: AccountId, amount: Money):
|
|
277
|
+
pass
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Smells Removed (after debate)
|
|
283
|
+
|
|
284
|
+
The following concepts were removed after an Advocate/Skeptic/Judge debate:
|
|
285
|
+
|
|
286
|
+
| Original smell | Why it was removed |
|
|
287
|
+
|---------------|--------------------|
|
|
288
|
+
| Parallel Inheritance Hierarchies | Deep inheritance is rare in modern codebases |
|
|
289
|
+
| Lazy Class | Conflicts with SRP; small classes can be good design |
|
|
290
|
+
| Speculative Generality | Too subjective to enforce consistently |
|
|
291
|
+
| Temporary Field | Low frequency in typical codebases |
|
|
292
|
+
| Message Chains | Fluent functional chains are common now |
|
|
293
|
+
| Middle Man | Can be a valid anti-corruption layer in layered designs |
|
|
294
|
+
| Alternative Classes with Different Interfaces | Typically covered by terminology/interface consistency checks |
|
|
295
|
+
| Incomplete Library Class | Third-party code can’t be refactored |
|
|
296
|
+
| Data Class | DTO layers often intentionally allow anemic structures |
|
|
297
|
+
| Refused Bequest | Inheritance usage has declined significantly |
|
|
298
|
+
| Comments | “Why” comments can be valuable |
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Quick Decision Flow
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
Spot suspicious code
|
|
306
|
+
|
|
|
307
|
+
+-- Duplicate? ------------------> Extract Method
|
|
308
|
+
|
|
|
309
|
+
+-- Function > 50 lines? --------> Extract Method + Decompose Conditional
|
|
310
|
+
|
|
|
311
|
+
+-- Class > 500 lines? ----------> Extract Class
|
|
312
|
+
|
|
|
313
|
+
+-- Parameters > 5? -------------> Introduce Parameter Object
|
|
314
|
+
|
|
|
315
|
+
+-- One change touches many? ----> Move Method/Field (centralize logic)
|
|
316
|
+
|
|
|
317
|
+
+-- One class changes for many reasons?
|
|
318
|
+
| -> Extract Class (split change axes)
|
|
319
|
+
|
|
|
320
|
+
+-- Method uses other class a lot?
|
|
321
|
+
| -> Move Method
|
|
322
|
+
|
|
|
323
|
+
+-- Domain concepts are strings?
|
|
324
|
+
-> Replace Data Value with Object
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## 9. Module Cycle
|
|
330
|
+
|
|
331
|
+
> Source: *Clean Architecture* (debate-revised) — a consensus “keep” rule
|
|
332
|
+
|
|
333
|
+
**Signals:**
|
|
334
|
+
- Module A depends on B, and B depends on A (direct cycle)
|
|
335
|
+
- Indirect cycles like A→B→C→A
|
|
336
|
+
- You can’t build/test a module independently
|
|
337
|
+
- “Changing one module forces changing another”
|
|
338
|
+
|
|
339
|
+
**Why it matters:**
|
|
340
|
+
- Blocks independent testing (you must mock multiple sides)
|
|
341
|
+
- Blocks independent deployment/release
|
|
342
|
+
- Strong signal of architecture erosion
|
|
343
|
+
- Refactor cost grows nonlinearly
|
|
344
|
+
|
|
345
|
+
**Detection tools:**
|
|
346
|
+
```bash
|
|
347
|
+
# JavaScript/TypeScript
|
|
348
|
+
npx madge --circular src/
|
|
349
|
+
|
|
350
|
+
# Java
|
|
351
|
+
jdeps -R -summary target/classes | grep cycle
|
|
352
|
+
|
|
353
|
+
# Go
|
|
354
|
+
go mod graph | tsort 2>&1 | grep -i cycle
|
|
355
|
+
|
|
356
|
+
# Python
|
|
357
|
+
pydeps --show-cycles src/
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Refactorings:**
|
|
361
|
+
1. **Dependency inversion** → extract interfaces into a separate module; both sides depend on the interface
|
|
362
|
+
2. **Callbacks/events** → A calls B, and B notifies A via callback/event instead of direct dependency
|
|
363
|
+
3. **Extract a shared module** → move common dependencies into module C
|
|
364
|
+
|
|
365
|
+
**Example:**
|
|
366
|
+
```python
|
|
367
|
+
# Smell: cyclic imports
|
|
368
|
+
# order.py
|
|
369
|
+
from payment import PaymentService # Order → Payment
|
|
370
|
+
|
|
371
|
+
# payment.py
|
|
372
|
+
from order import Order # Payment → Order (cycle!)
|
|
373
|
+
|
|
374
|
+
# Refactor: dependency inversion
|
|
375
|
+
# interfaces.py (independent module)
|
|
376
|
+
class OrderInterface(ABC):
|
|
377
|
+
@abstractmethod
|
|
378
|
+
def get_total(self) -> Money: ...
|
|
379
|
+
|
|
380
|
+
# order.py
|
|
381
|
+
class Order(OrderInterface): # implements the interface
|
|
382
|
+
pass
|
|
383
|
+
|
|
384
|
+
# payment.py
|
|
385
|
+
from interfaces import OrderInterface # depend on the interface only
|
|
386
|
+
class PaymentService:
|
|
387
|
+
def charge(self, order: OrderInterface): ...
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**CI integration suggestion:**
|
|
391
|
+
```yaml
|
|
392
|
+
# .github/workflows/ci.yml
|
|
393
|
+
- name: Check circular dependencies
|
|
394
|
+
run: |
|
|
395
|
+
npx madge --circular src/ && echo "No cycles found" || exit 1
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## References
|
|
401
|
+
|
|
402
|
+
- *Refactoring: Improving the Design of Existing Code* (2nd Edition) — Martin Fowler
|
|
403
|
+
- *Clean Architecture* — Robert C. Martin (Chapter: Component Coupling)
|
|
404
|
+
- Dev Playbooks debate-revised evaluation notes
|
|
405
|
+
- `devbooks-code-review` checklists
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## 10. VS Code-style Code Hygiene Checks
|
|
410
|
+
|
|
411
|
+
> Inspired by VS Code custom ESLint rules as automated review items.
|
|
412
|
+
|
|
413
|
+
### Do-not-commit patterns (Blocker)
|
|
414
|
+
|
|
415
|
+
| Pattern | Detection command | Why it matters |
|
|
416
|
+
|---------|-------------------|----------------|
|
|
417
|
+
| `test.only` / `describe.only` | `rg '\\.only\\s*\\(' tests/` | Skips other tests |
|
|
418
|
+
| `console.log` / `console.debug` | `rg 'console\\.(log\\|debug)' src/` | Debug leftovers |
|
|
419
|
+
| `debugger` | `rg 'debugger' src/` | Breakpoints left in code |
|
|
420
|
+
| `@ts-ignore` | `rg '@ts-ignore' src/` | Hides type errors |
|
|
421
|
+
| `as any` | `rg 'as any' src/` | Bypasses type safety |
|
|
422
|
+
| `TODO` without an issue | `rg 'TODO(?!.*#\\d+)' src/` | Untracked work |
|
|
423
|
+
|
|
424
|
+
### Resource management checks (Warning)
|
|
425
|
+
|
|
426
|
+
| Pattern | How to detect | Preferred fix |
|
|
427
|
+
|---------|---------------|---------------|
|
|
428
|
+
| Non-`readonly` `DisposableStore` | `rg 'private\\s+(?!readonly)\\s*_?\\w*[Dd]isposable'` | Use `readonly` |
|
|
429
|
+
| `dispose()` missing `super.dispose()` | Review `override dispose` | Must call `super.dispose()` |
|
|
430
|
+
| `setInterval` without cleanup | Find `setInterval` without `clearInterval` | Clear in `dispose()` |
|
|
431
|
+
| Listener without removal | Find `addEventListener` without `removeEventListener` | Use `AbortController` |
|
|
432
|
+
|
|
433
|
+
### Layering constraints checks
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
# Verify forbidden cross-layer imports
|
|
437
|
+
# base must not depend on platform/editor/workbench
|
|
438
|
+
rg "from ['\\\"](vs/(platform|editor|workbench))" src/vs/base/
|
|
439
|
+
|
|
440
|
+
# platform must not depend on editor/workbench
|
|
441
|
+
rg "from ['\\\"](vs/(editor|workbench))" src/vs/platform/
|
|
442
|
+
|
|
443
|
+
# common must not depend on browser/node
|
|
444
|
+
rg "from ['\\\"].*(browser|node)" src/**/common/
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Type safety checks
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
// Avoid: empty-object assertions
|
|
451
|
+
const config = {} as Config; // bad
|
|
452
|
+
|
|
453
|
+
// Avoid: non-null assertions
|
|
454
|
+
const name = user!.name; // bad
|
|
455
|
+
|
|
456
|
+
// Avoid: any
|
|
457
|
+
function process(data: any) { } // bad
|
|
458
|
+
|
|
459
|
+
// Prefer: unknown or concrete types
|
|
460
|
+
function process(data: unknown) { } // ok
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Automation script example
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
#!/usr/bin/env bash
|
|
467
|
+
# hygiene-check.sh
|
|
468
|
+
|
|
469
|
+
set -euo pipefail
|
|
470
|
+
|
|
471
|
+
echo "=== Code hygiene checks ==="
|
|
472
|
+
|
|
473
|
+
# 1) Debugging statements
|
|
474
|
+
if rg -l 'console\\.(log|debug)|debugger' src/ --type ts 2>/dev/null; then
|
|
475
|
+
echo "ERROR: found debugging statements"
|
|
476
|
+
exit 1
|
|
477
|
+
fi
|
|
478
|
+
|
|
479
|
+
# 2) test.only / describe.only
|
|
480
|
+
if rg -l '\\.only\\s*\\(' tests/ --type ts 2>/dev/null; then
|
|
481
|
+
echo "ERROR: found test.only / describe.only"
|
|
482
|
+
exit 1
|
|
483
|
+
fi
|
|
484
|
+
|
|
485
|
+
# 3) @ts-ignore occurrences
|
|
486
|
+
count=$(rg -c '@ts-ignore' src/ --type ts 2>/dev/null | wc -l)
|
|
487
|
+
if [ "${count}" -gt 0 ]; then
|
|
488
|
+
echo "WARN: found ${count} files with @ts-ignore"
|
|
489
|
+
fi
|
|
490
|
+
|
|
491
|
+
# 4) any occurrences
|
|
492
|
+
count=$(rg -c ': any[^a-z]' src/ --type ts 2>/dev/null | wc -l)
|
|
493
|
+
if [ "${count}" -gt 0 ]; then
|
|
494
|
+
echo "WARN: found ${count} files with ': any'"
|
|
495
|
+
fi
|
|
496
|
+
|
|
497
|
+
echo "OK: hygiene checks passed"
|
|
498
|
+
```
|