dev-playbooks-cn 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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +466 -0
  3. package/bin/devbooks.js +987 -0
  4. package/package.json +43 -0
  5. package/skills/Skills/344/275/277/347/224/250/350/257/264/346/230/216.md +446 -0
  6. package/skills/Skill/345/274/200/345/217/221/346/214/207/345/215/227.md +248 -0
  7. package/skills/_shared/context-detection-template.md +315 -0
  8. package/skills/_shared/mcp-enhancement-template.md +144 -0
  9. package/skills/_shared/references//351/200/232/347/224/250/345/256/210/351/227/250/345/215/217/350/256/256.md +114 -0
  10. package/skills/_template/config-discovery-template.md +126 -0
  11. package/skills/devbooks-brownfield-bootstrap/SKILL.md +167 -0
  12. package/skills/devbooks-brownfield-bootstrap/references//344/273/243/347/240/201/345/257/274/350/210/252/347/255/226/347/225/245.md +203 -0
  13. package/skills/devbooks-brownfield-bootstrap/references//345/255/230/351/207/217/351/241/271/347/233/256/345/210/235/345/247/213/345/214/226.md +96 -0
  14. package/skills/devbooks-brownfield-bootstrap/references//345/255/230/351/207/217/351/241/271/347/233/256/345/210/235/345/247/213/345/214/226/346/217/220/347/244/272/350/257/215.md +115 -0
  15. package/skills/devbooks-brownfield-bootstrap/references//346/234/257/350/257/255/350/241/250/346/250/241/346/235/277.md +42 -0
  16. package/skills/devbooks-brownfield-bootstrap/scripts/cod-update.sh +357 -0
  17. package/skills/devbooks-brownfield-bootstrap/templates/project-profile-template.md +172 -0
  18. package/skills/devbooks-c4-map/SKILL.md +151 -0
  19. package/skills/devbooks-c4-map/references/C4/346/236/266/346/236/204/345/234/260/345/233/276/346/217/220/347/244/272/350/257/215.md +33 -0
  20. package/skills/devbooks-c4-map/references//345/210/206/345/261/202/347/272/246/346/235/237/346/243/200/346/237/245/346/270/205/345/215/225.md +185 -0
  21. package/skills/devbooks-code-review/SKILL.md +175 -0
  22. package/skills/devbooks-code-review/references/PR/346/250/241/346/235/277/344/270/216/346/214/207/345/215/227.md +321 -0
  23. package/skills/devbooks-code-review/references//344/273/243/347/240/201/350/257/204/345/256/241/346/217/220/347/244/272/350/257/215.md +100 -0
  24. package/skills/devbooks-code-review/references//345/235/217/345/221/263/351/201/223/351/200/237/346/237/245/350/241/250.md +495 -0
  25. package/skills/devbooks-code-review/references//350/265/204/346/272/220/347/256/241/347/220/206/345/256/241/346/237/245/346/270/205/345/215/225.md +311 -0
  26. package/skills/devbooks-coder/SKILL.md +219 -0
  27. package/skills/devbooks-coder/references//344/273/243/347/240/201/345/256/236/347/216/260/346/217/220/347/244/272/350/257/215.md +70 -0
  28. package/skills/devbooks-coder/references//344/275/216/351/243/216/351/231/251/346/224/271/345/212/250/346/212/200/346/234/257.md +275 -0
  29. package/skills/devbooks-coder/references//346/227/245/345/277/227/350/247/204/350/214/203.md +329 -0
  30. package/skills/devbooks-coder/references//347/274/226/347/240/201/351/243/216/346/240/274/347/273/206/345/210/231.md +351 -0
  31. package/skills/devbooks-coder/references//351/224/231/350/257/257/347/240/201/350/247/204/350/214/203.md +463 -0
  32. package/skills/devbooks-delivery-workflow/SKILL.md +217 -0
  33. package/skills/devbooks-delivery-workflow/references//344/272/244/344/273/230/351/252/214/346/224/266/345/267/245/344/275/234/346/265/201.md +256 -0
  34. package/skills/devbooks-delivery-workflow/references//345/216/237/345/236/213-/347/224/237/344/272/247/345/217/214/350/275/250/346/250/241/345/274/217.md +168 -0
  35. package/skills/devbooks-delivery-workflow/references//345/217/230/346/233/264/351/252/214/350/257/201/344/270/216/350/277/275/346/272/257/346/250/241/346/235/277.md +133 -0
  36. package/skills/devbooks-delivery-workflow/scripts/ac-trace-check.sh +330 -0
  37. package/skills/devbooks-delivery-workflow/scripts/audit-scope.sh +262 -0
  38. package/skills/devbooks-delivery-workflow/scripts/change-check.sh +1040 -0
  39. package/skills/devbooks-delivery-workflow/scripts/change-codemod-scaffold.sh +135 -0
  40. package/skills/devbooks-delivery-workflow/scripts/change-evidence.sh +152 -0
  41. package/skills/devbooks-delivery-workflow/scripts/change-scaffold.sh +442 -0
  42. package/skills/devbooks-delivery-workflow/scripts/change-spec-delta-scaffold.sh +136 -0
  43. package/skills/devbooks-delivery-workflow/scripts/constitution-check.sh +237 -0
  44. package/skills/devbooks-delivery-workflow/scripts/env-match-check.sh +128 -0
  45. package/skills/devbooks-delivery-workflow/scripts/fitness-check.sh +387 -0
  46. package/skills/devbooks-delivery-workflow/scripts/guardrail-check.sh +519 -0
  47. package/skills/devbooks-delivery-workflow/scripts/handoff-check.sh +141 -0
  48. package/skills/devbooks-delivery-workflow/scripts/hygiene-check.sh +340 -0
  49. package/skills/devbooks-delivery-workflow/scripts/migrate-from-openspec.sh +385 -0
  50. package/skills/devbooks-delivery-workflow/scripts/migrate-to-v2-gates.sh +202 -0
  51. package/skills/devbooks-delivery-workflow/scripts/progress-dashboard.sh +319 -0
  52. package/skills/devbooks-delivery-workflow/scripts/prototype-promote.sh +341 -0
  53. package/skills/devbooks-delivery-workflow/scripts/spec-preview.sh +203 -0
  54. package/skills/devbooks-delivery-workflow/scripts/spec-promote.sh +118 -0
  55. package/skills/devbooks-delivery-workflow/scripts/spec-rollback.sh +124 -0
  56. package/skills/devbooks-delivery-workflow/scripts/spec-stage.sh +117 -0
  57. package/skills/devbooks-delivery-workflow/scripts/verify-all.sh +78 -0
  58. package/skills/devbooks-delivery-workflow/scripts/verify-npm-package.sh +123 -0
  59. package/skills/devbooks-delivery-workflow/scripts/verify-openspec-free.sh +81 -0
  60. package/skills/devbooks-delivery-workflow/scripts/verify-slash-commands.sh +146 -0
  61. package/skills/devbooks-delivery-workflow/templates/handoff.md +50 -0
  62. package/skills/devbooks-design-backport/SKILL.md +73 -0
  63. package/skills/devbooks-design-backport/references//345/233/236/345/206/231/350/256/276/350/256/241/346/226/207/346/241/243/346/217/220/347/244/272/350/257/215.md +196 -0
  64. package/skills/devbooks-design-doc/SKILL.md +121 -0
  65. package/skills/devbooks-design-doc/references//345/276/256/346/234/215/345/212/241/350/256/276/350/256/241/346/270/205/345/215/225.md +149 -0
  66. package/skills/devbooks-design-doc/references//350/256/276/350/256/241/346/226/207/346/241/243/346/217/220/347/244/272/350/257/215.md +189 -0
  67. package/skills/devbooks-design-doc/references//351/232/220/347/247/201/345/220/210/350/247/204/346/243/200/346/237/245/346/270/205/345/215/225.md +240 -0
  68. package/skills/devbooks-entropy-monitor/SKILL.md +188 -0
  69. package/skills/devbooks-entropy-monitor/references//347/206/265/345/272/246/351/207/217/346/226/271/346/263/225/350/256/272.md +223 -0
  70. package/skills/devbooks-entropy-monitor/scripts/entropy-measure.sh +449 -0
  71. package/skills/devbooks-entropy-monitor/scripts/entropy-report.sh +303 -0
  72. package/skills/devbooks-entropy-monitor/templates/thresholds.json +99 -0
  73. package/skills/devbooks-federation/SKILL.md +264 -0
  74. package/skills/devbooks-federation/scripts/federation-check.sh +144 -0
  75. package/skills/devbooks-federation/templates/federation.yaml +89 -0
  76. package/skills/devbooks-impact-analysis/SKILL.md +135 -0
  77. package/skills/devbooks-impact-analysis/references//345/275/261/345/223/215/345/210/206/346/236/220/346/217/220/347/244/272/350/257/215.md +82 -0
  78. package/skills/devbooks-impact-analysis/scripts/graph-cache.sh +214 -0
  79. package/skills/devbooks-implementation-plan/SKILL.md +83 -0
  80. package/skills/devbooks-implementation-plan/references//347/274/226/347/240/201/350/256/241/345/210/222/346/217/220/347/244/272/350/257/215.md +99 -0
  81. package/skills/devbooks-index-bootstrap/SKILL.md +240 -0
  82. package/skills/devbooks-proposal-author/SKILL.md +83 -0
  83. package/skills/devbooks-proposal-author/references//346/217/220/346/241/210/346/222/260/345/206/231/346/217/220/347/244/272/350/257/215.md +66 -0
  84. package/skills/devbooks-proposal-challenger/SKILL.md +86 -0
  85. package/skills/devbooks-proposal-challenger/references//344/274/246/347/220/206/344/270/216/345/220/210/350/247/204/346/243/200/346/237/245/346/270/205/345/215/225.md +176 -0
  86. package/skills/devbooks-proposal-challenger/references//346/217/220/346/241/210/350/264/250/347/226/221/346/217/220/347/244/272/350/257/215.md +57 -0
  87. package/skills/devbooks-proposal-debate-workflow/SKILL.md +78 -0
  88. package/skills/devbooks-proposal-debate-workflow/references//346/217/220/346/241/210/345/257/271/350/276/251/345/267/245/344/275/234/346/265/201.md +24 -0
  89. package/skills/devbooks-proposal-debate-workflow/references//346/217/220/346/241/210/345/257/271/350/276/251/346/250/241/346/235/277.md +35 -0
  90. package/skills/devbooks-proposal-debate-workflow/scripts/proposal-debate-check.sh +102 -0
  91. package/skills/devbooks-proposal-judge/SKILL.md +78 -0
  92. package/skills/devbooks-proposal-judge/references//346/217/220/346/241/210/350/243/201/345/206/263/346/217/220/347/244/272/350/257/215.md +37 -0
  93. package/skills/devbooks-router/SKILL.md +346 -0
  94. package/skills/devbooks-spec-contract/SKILL.md +191 -0
  95. package/skills/devbooks-spec-contract/references/API/350/256/276/350/256/241/346/214/207/345/215/227.md +349 -0
  96. package/skills/devbooks-spec-contract/references//345/245/221/347/272/246/344/270/216/346/225/260/346/215/256/345/256/232/344/271/211/346/217/220/347/244/272/350/257/215.md +85 -0
  97. package/skills/devbooks-spec-contract/references//350/247/204/346/240/274/345/217/230/346/233/264/346/217/220/347/244/272/350/257/215.md +63 -0
  98. package/skills/devbooks-spec-contract/references//351/232/220/345/274/217/345/217/230/346/233/264/346/243/200/346/265/213/346/217/220/347/244/272/350/257/215.md +183 -0
  99. package/skills/devbooks-spec-contract/scripts/implicit-change-detect.sh +378 -0
  100. package/skills/devbooks-spec-gardener/SKILL.md +72 -0
  101. package/skills/devbooks-spec-gardener/references//350/247/204/346/240/274/345/233/255/344/270/201/346/217/220/347/244/272/350/257/215.md +41 -0
  102. package/skills/devbooks-test-owner/SKILL.md +172 -0
  103. package/skills/devbooks-test-owner/references//345/217/230/346/233/264/351/252/214/350/257/201/344/270/216/350/277/275/346/272/257/346/250/241/346/235/277.md +228 -0
  104. package/skills/devbooks-test-owner/references//345/274/202/346/255/245/347/263/273/347/273/237/346/265/213/350/257/225/347/255/226/347/225/245.md +316 -0
  105. package/skills/devbooks-test-owner/references//346/265/213/350/257/225/344/273/243/347/240/201/346/217/220/347/244/272/350/257/215.md +208 -0
  106. package/skills/devbooks-test-owner/references//346/265/213/350/257/225/345/210/206/345/261/202/347/255/226/347/225/245.md +281 -0
  107. package/skills/devbooks-test-owner/references//346/265/213/350/257/225/351/251/261/345/212/250.md +394 -0
  108. package/skills/devbooks-test-owner/references//350/247/243/344/276/235/350/265/226/346/212/200/346/234/257/351/200/237/346/237/245/350/241/250.md +432 -0
  109. package/skills/devbooks-test-reviewer/SKILL.md +189 -0
  110. package/templates/.devbooks/config.yaml +88 -0
  111. package/templates/claude-commands/devbooks/apply.md +38 -0
  112. package/templates/claude-commands/devbooks/archive.md +33 -0
  113. package/templates/claude-commands/devbooks/backport.md +19 -0
  114. package/templates/claude-commands/devbooks/bootstrap.md +19 -0
  115. package/templates/claude-commands/devbooks/c4.md +19 -0
  116. package/templates/claude-commands/devbooks/challenger.md +19 -0
  117. package/templates/claude-commands/devbooks/code.md +19 -0
  118. package/templates/claude-commands/devbooks/debate.md +19 -0
  119. package/templates/claude-commands/devbooks/delivery.md +19 -0
  120. package/templates/claude-commands/devbooks/design.md +19 -0
  121. package/templates/claude-commands/devbooks/entropy.md +19 -0
  122. package/templates/claude-commands/devbooks/federation.md +19 -0
  123. package/templates/claude-commands/devbooks/gardener.md +19 -0
  124. package/templates/claude-commands/devbooks/impact.md +19 -0
  125. package/templates/claude-commands/devbooks/index.md +19 -0
  126. package/templates/claude-commands/devbooks/judge.md +19 -0
  127. package/templates/claude-commands/devbooks/plan.md +19 -0
  128. package/templates/claude-commands/devbooks/proposal.md +19 -0
  129. package/templates/claude-commands/devbooks/quick.md +42 -0
  130. package/templates/claude-commands/devbooks/review.md +19 -0
  131. package/templates/claude-commands/devbooks/router.md +19 -0
  132. package/templates/claude-commands/devbooks/spec.md +19 -0
  133. package/templates/claude-commands/devbooks/test-review.md +19 -0
  134. package/templates/claude-commands/devbooks/test.md +19 -0
  135. package/templates/dev-playbooks/README.md +458 -0
  136. package/templates/dev-playbooks/changes/.gitkeep +1 -0
  137. package/templates/dev-playbooks/constitution.md +116 -0
  138. package/templates/dev-playbooks/project.md +96 -0
  139. package/templates/dev-playbooks/scripts/.gitkeep +1 -0
  140. package/templates/dev-playbooks/specs/_meta/anti-patterns/.gitkeep +2 -0
  141. package/templates/dev-playbooks/specs/_meta/glossary.md +47 -0
  142. package/templates/dev-playbooks/specs/_meta/project-profile.md +79 -0
  143. package/templates/dev-playbooks/specs/architecture/fitness-rules.md +95 -0
@@ -0,0 +1,495 @@
1
+ # 8 种核心坏味道速查表
2
+
3
+ > 来源:《重构:改善既有代码的设计》辩论修订版
4
+ > 从原书 22 种坏味道精简为 8 种高频高影响的核心坏味道
5
+
6
+ ---
7
+
8
+ ## 速查索引
9
+
10
+ | # | 坏味道 | 一句话描述 | 严重性 |
11
+ |---|--------|------------|--------|
12
+ | 1 | Duplicated Code | 相同代码出现多处 | 阻塞 |
13
+ | 2 | Long Method | 函数太长难以理解 | 阻塞 |
14
+ | 3 | Large Class | 类承担太多职责 | 警告 |
15
+ | 4 | Long Parameter List | 参数太多难以调用 | 阻塞 |
16
+ | 5 | Divergent Change | 一个类因多种原因变化 | 警告 |
17
+ | 6 | Shotgun Surgery | 一个变更需改多个类 | 阻塞 |
18
+ | 7 | Feature Envy | 函数过度依赖他类 | 警告 |
19
+ | 8 | Primitive Obsession | 业务概念用原始类型 | 警告 |
20
+ | 9 | **Module Cycle(循环依赖)** | A→B→A 导致无法独立测试 | **阻塞** |
21
+
22
+ ---
23
+
24
+ ## 1. Duplicated Code(重复代码)
25
+
26
+ **识别信号**:
27
+ - 相似度>80%的代码块出现≥2处
28
+ - 复制粘贴后只改了变量名
29
+ - 兄弟子类中有相同代码
30
+
31
+ **为什么是问题**:
32
+ - 修改时容易漏改,导致行为不一致
33
+ - 增加代码量,降低可读性
34
+ - 违反 DRY 原则
35
+
36
+ **重构手法**:
37
+ 1. **同一类内重复** → Extract Method
38
+ 2. **兄弟子类间重复** → Extract Method → Pull Up Method
39
+ 3. **无关类间重复** → Extract Class(提取公共类)
40
+
41
+ **代码示例**:
42
+ ```python
43
+ # 坏味道:重复的验证逻辑
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: # 重复!
51
+ raise ValueError("Invalid email")
52
+ # ...
53
+
54
+ # 重构后:提取方法
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
+ **识别信号**:
69
+ - **P95<50行**(超标触发讨论)
70
+ - 需要滚动才能看完整个函数
71
+ - 函数内有大段注释解释"这部分做什么"
72
+ - 圈复杂度>10
73
+
74
+ **为什么是问题**:
75
+ - 难以理解函数整体逻辑
76
+ - 难以测试(需要覆盖太多分支)
77
+ - 难以复用部分逻辑
78
+
79
+ **重构手法**:
80
+ 1. **注释是信号** → Extract Method(函数名来自注释)
81
+ 2. **临时变量过多** → Replace Temp with Query
82
+ 3. **条件分支复杂** → Decompose Conditional
83
+
84
+ **代码示例**:
85
+ ```python
86
+ # 坏味道:过长函数
87
+ def process_order(order):
88
+ # 验证订单
89
+ if not order.items:
90
+ raise ValueError("Empty order")
91
+ if order.total < 0:
92
+ raise ValueError("Invalid total")
93
+
94
+ # 计算折扣
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
+ # 更新库存
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行
108
+
109
+ # 重构后:提取方法
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
+ **识别信号**:
122
+ - **P95<500行**
123
+ - 实例变量>10个
124
+ - 方法>20个
125
+ - 有多组字段名前缀相同(如 `billing_xxx`, `shipping_xxx`)
126
+
127
+ **为什么是问题**:
128
+ - 违反单一职责原则
129
+ - 难以测试(依赖太多)
130
+ - 修改时影响范围不可控
131
+
132
+ **重构手法**:
133
+ 1. **职责可分** → Extract Class
134
+ 2. **存在子类型** → Extract Subclass
135
+ 3. **只需部分接口** → Extract Interface
136
+
137
+ ---
138
+
139
+ ## 4. Long Parameter List(过长参数列)
140
+
141
+ **识别信号**:
142
+ - 参数数量>5
143
+ - 参数顺序容易搞混
144
+ - 多个函数有相同的参数组合
145
+
146
+ **为什么是问题**:
147
+ - 调用时容易传错参数
148
+ - 函数签名难以记忆
149
+ - 参数组合可能是隐藏的概念
150
+
151
+ **重构手法**:
152
+ 1. **参数可从对象获取** → Preserve Whole Object
153
+ 2. **参数总是一起出现** → Introduce Parameter Object
154
+
155
+ **代码示例**:
156
+ ```python
157
+ # 坏味道:参数过多
158
+ def create_address(street, city, state, zip_code, country, apt_number):
159
+ pass
160
+
161
+ # 重构后:引入参数对象
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
+ **识别信号**:
180
+ - 修改数据库时要改这个类
181
+ - 修改 UI 时也要改这个类
182
+ - 修改业务规则时还要改这个类
183
+ - "每次改需求都要改这个文件"
184
+
185
+ **为什么是问题**:
186
+ - 类承担了多个变化轴的职责
187
+ - 不同原因的修改互相影响
188
+ - 难以单独测试某个维度
189
+
190
+ **重构手法**:
191
+ - Extract Class(按变化原因分离)
192
+
193
+ **对比 Shotgun Surgery**:
194
+ - Divergent Change:一个类响应多种变化
195
+ - Shotgun Surgery:一种变化需要改多个类
196
+ - 两者是**对偶问题**,解法相反
197
+
198
+ ---
199
+
200
+ ## 6. Shotgun Surgery(霰弹式修改)
201
+
202
+ **识别信号**:
203
+ - 一个需求需要修改≥3个类
204
+ - "改一处要改好多地方"
205
+ - 容易漏改导致 bug
206
+
207
+ **为什么是问题**:
208
+ - 修改容易遗漏
209
+ - 散落的逻辑难以理解整体
210
+ - 测试覆盖困难
211
+
212
+ **重构手法**:
213
+ 1. **逻辑应集中** → Move Method / Move Field
214
+ 2. **过度分散** → Inline Class(先合并再重新拆分)
215
+
216
+ ---
217
+
218
+ ## 7. Feature Envy(依恋情结)
219
+
220
+ **识别信号**:
221
+ - 函数对其他类的调用 > 对自己类的调用
222
+ - 函数大量使用另一个类的字段
223
+ - "这个方法好像放错地方了"
224
+
225
+ **为什么是问题**:
226
+ - 违反"数据和操作应该在一起"原则
227
+ - 增加类之间的耦合
228
+ - 职责划分不清晰
229
+
230
+ **重构手法**:
231
+ - Move Method(移到数据所在的类)
232
+
233
+ **例外情况**(不是 Feature Envy):
234
+ - Strategy 模式(策略类访问上下文)
235
+ - Visitor 模式(访问者访问元素)
236
+ - 需要在代码注释中标注"这是设计模式,不是 Feature Envy"
237
+
238
+ ---
239
+
240
+ ## 8. Primitive Obsession(基本类型偏执)
241
+
242
+ **识别信号**:
243
+ - 用 String 存电话号码、邮箱、货币
244
+ - 用 int 存状态码、类型码
245
+ - 业务规则散落在多处(如邮箱格式验证)
246
+
247
+ **为什么是问题**:
248
+ - 缺乏类型安全(String 可以传任何值)
249
+ - 业务规则无法集中
250
+ - 与 glossary.md 术语不对齐
251
+
252
+ **重构手法**:
253
+ - Replace Data Value with Object
254
+ - Replace Type Code with Class/Subclass
255
+
256
+ **适用范围**(辩论修订):
257
+ - **必须封装**:业务概念(Money、Email、UserId、PhoneNumber)
258
+ - **可选封装**:技术类型(坐标、颜色、简单配置)
259
+
260
+ **代码示例**:
261
+ ```python
262
+ # 坏味道:原始类型存储业务概念
263
+ def transfer(from_account: str, to_account: str, amount: float):
264
+ pass # amount 可以是负数?什么货币?
265
+
266
+ # 重构后:封装为值对象
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
+ ## 已删除的坏味道(辩论后精简)
283
+
284
+ 以下概念经 Advocate/Skeptic/Judge 三方辩论后被删除:
285
+
286
+ | 原坏味道 | 删除理由 |
287
+ |----------|----------|
288
+ | Parallel Inheritance Hierarchies | 现代代码极少使用深层继承 |
289
+ | Lazy Class | 与 SRP 冲突,小类是好设计 |
290
+ | Speculative Generality | 判断标准过于主观 |
291
+ | Temporary Field | 发生率低 |
292
+ | Message Chains | 函数式链式调用盛行 |
293
+ | Middle Man | 分层架构中有防腐价值 |
294
+ | Alternative Classes with Different Interfaces | 被术语一致性检查覆盖 |
295
+ | Incomplete Library Class | 第三方库无法重构 |
296
+ | Data Class | 现代架构(DDD)中 DTO 层允许贫血 |
297
+ | Refused Bequest | 继承使用已大幅减少 |
298
+ | Comments | "Why"型注释有价值 |
299
+
300
+ ---
301
+
302
+ ## 快速决策流程图
303
+
304
+ ```
305
+ 发现可疑代码
306
+
307
+ ├─ 重复? ──────────────→ Extract Method
308
+
309
+ ├─ 函数>50行? ─────────→ Extract Method + Decompose Conditional
310
+
311
+ ├─ 类>500行? ──────────→ Extract Class
312
+
313
+ ├─ 参数>5个? ──────────→ Introduce Parameter Object
314
+
315
+ ├─ 改一处要改多处? ────→ Move Method/Field(集中逻辑)
316
+
317
+ ├─ 一个类响应多种变化? → Extract Class(分离变化轴)
318
+
319
+ ├─ 函数总访问别的类? ──→ Move Method
320
+
321
+ └─ 业务概念用String? ──→ Replace Data Value with Object
322
+ ```
323
+
324
+ ---
325
+
326
+ ## 9. Module Cycle(循环依赖)
327
+
328
+ > 来源:《架构整洁之道》辩论修订版 - 双方共识保留的核心规则
329
+
330
+ **识别信号**:
331
+ - 模块 A 依赖 B,B 又依赖 A(直接循环)
332
+ - A→B→C→A 的间接循环
333
+ - 无法单独编译/测试某个模块
334
+ - "改一个模块,另一个必须同时改"
335
+
336
+ **为什么是问题**:
337
+ - 模块无法独立测试(必须同时 mock 多侧)
338
+ - 无法独立部署/发布
339
+ - 架构腐化的强信号
340
+ - 重构成本指数级增长
341
+
342
+ **检测工具**:
343
+ ```bash
344
+ # JavaScript/TypeScript
345
+ npx madge --circular src/
346
+
347
+ # Java
348
+ jdeps -R -summary target/classes | grep cycle
349
+
350
+ # Go
351
+ go mod graph | tsort 2>&1 | grep -i cycle
352
+
353
+ # Python
354
+ pydeps --show-cycles src/
355
+ ```
356
+
357
+ **重构手法**:
358
+ 1. **依赖倒置** → 提取接口到独立模块,双方都依赖接口
359
+ 2. **回调/事件** → A 调用 B 时,B 通过回调/事件通知 A,而非直接依赖
360
+ 3. **提取公共模块** → 把 A/B 共同依赖的部分提取为 C
361
+
362
+ **代码示例**:
363
+ ```python
364
+ # 坏味道:循环依赖
365
+ # order.py
366
+ from payment import PaymentService # Order → Payment
367
+
368
+ # payment.py
369
+ from order import Order # Payment → Order(循环!)
370
+
371
+ # 重构后:依赖倒置
372
+ # interfaces.py(独立模块)
373
+ class OrderInterface(ABC):
374
+ @abstractmethod
375
+ def get_total(self) -> Money: pass
376
+
377
+ # order.py
378
+ class Order(OrderInterface): # 实现接口
379
+ pass
380
+
381
+ # payment.py
382
+ from interfaces import OrderInterface # 只依赖接口
383
+ class PaymentService:
384
+ def charge(self, order: OrderInterface): pass
385
+ ```
386
+
387
+ **CI 集成建议**:
388
+ ```yaml
389
+ # .github/workflows/ci.yml
390
+ - name: Check circular dependencies
391
+ run: |
392
+ npx madge --circular src/ && echo "No cycles found" || exit 1
393
+ ```
394
+
395
+ ---
396
+
397
+ ## 参考资料
398
+
399
+ - 《重构:改善既有代码的设计》(第2版) - Martin Fowler
400
+ - 《架构整洁之道》- Robert C. Martin(第14章 组件耦合)
401
+ - dev-playbooks 辩论修订版评估报告
402
+ - devbooks-code-review 检查清单
403
+
404
+ ---
405
+
406
+ ## 10. VS Code 风格代码卫生检查
407
+
408
+ > 借鉴 VS Code ESLint 自定义规则,作为代码评审的自动化检查项
409
+
410
+ ### 禁止提交的模式(阻塞级)
411
+
412
+ | 模式 | 检测命令 | 原因 |
413
+ |------|----------|------|
414
+ | `test.only` / `describe.only` | `rg '\.only\s*\(' tests/` | 跳过其他测试 |
415
+ | `console.log` / `console.debug` | `rg 'console\.(log\|debug)' src/` | 调试代码残留 |
416
+ | `debugger` | `rg 'debugger' src/` | 断点残留 |
417
+ | `@ts-ignore` | `rg '@ts-ignore' src/` | 隐藏类型错误 |
418
+ | `as any` | `rg 'as any' src/` | 类型安全绕过 |
419
+ | `TODO` 无 issue | `rg 'TODO(?!.*#\d+)' src/` | 无法追踪的待办 |
420
+
421
+ ### 资源管理检查(警告级)
422
+
423
+ | 模式 | 检测方法 | 正确做法 |
424
+ |------|----------|----------|
425
+ | 非 readonly DisposableStore | `rg 'private\s+(?!readonly)\s*_?\w*[Dd]isposable'` | 使用 `readonly` |
426
+ | dispose() 未调用 super | 人工检查 override dispose | 必须调用 `super.dispose()` |
427
+ | setInterval 无清理 | 搜索 setInterval 无对应 clearInterval | 在 dispose 中清理 |
428
+ | 事件监听无移除 | 搜索 addEventListener 无 removeEventListener | 使用 AbortController |
429
+
430
+ ### 分层约束检查
431
+
432
+ ```bash
433
+ # 检查禁止的跨层依赖
434
+ # base 层不能依赖 platform/editor/workbench
435
+ rg "from ['\"](vs/(platform|editor|workbench))" src/vs/base/
436
+
437
+ # platform 层不能依赖 editor/workbench
438
+ rg "from ['\"](vs/(editor|workbench))" src/vs/platform/
439
+
440
+ # common 层不能依赖 browser/node
441
+ rg "from ['\"].*(browser|node)" src/**/common/
442
+ ```
443
+
444
+ ### 类型安全检查
445
+
446
+ ```typescript
447
+ // 禁止:空对象断言
448
+ const config = {} as Config; // ❌
449
+
450
+ // 禁止:非空断言
451
+ const name = user!.name; // ❌
452
+
453
+ // 禁止:any 类型
454
+ function process(data: any) { } // ❌
455
+
456
+ // 正确:使用 unknown 或具体类型
457
+ function process(data: unknown) { } // ✓
458
+ ```
459
+
460
+ ### 自动化检查脚本
461
+
462
+ ```bash
463
+ #!/bin/bash
464
+ # hygiene-check.sh
465
+
466
+ set -e
467
+
468
+ echo "=== 代码卫生检查 ==="
469
+
470
+ # 1. 调试代码
471
+ if rg -l 'console\.(log|debug)|debugger' src/ --type ts 2>/dev/null; then
472
+ echo "❌ 发现调试代码"
473
+ exit 1
474
+ fi
475
+
476
+ # 2. test.only
477
+ if rg -l '\.only\s*\(' tests/ --type ts 2>/dev/null; then
478
+ echo "❌ 发现 test.only"
479
+ exit 1
480
+ fi
481
+
482
+ # 3. @ts-ignore
483
+ count=$(rg -c '@ts-ignore' src/ --type ts 2>/dev/null | wc -l)
484
+ if [ "$count" -gt 0 ]; then
485
+ echo "⚠️ 发现 $count 处 @ts-ignore"
486
+ fi
487
+
488
+ # 4. any 类型
489
+ count=$(rg -c ': any[^a-z]' src/ --type ts 2>/dev/null | wc -l)
490
+ if [ "$count" -gt 0 ]; then
491
+ echo "⚠️ 发现 $count 处 any 类型"
492
+ fi
493
+
494
+ echo "✅ 卫生检查通过"
495
+ ```