opencode-metis 0.1.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 (156) hide show
  1. package/README.md +140 -0
  2. package/dist/cli.cjs +63 -0
  3. package/dist/mcp-server.cjs +51 -0
  4. package/dist/plugin.cjs +4 -0
  5. package/dist/worker.cjs +224 -0
  6. package/opencode/agent/the-analyst/feature-prioritization.md +66 -0
  7. package/opencode/agent/the-analyst/market-research.md +77 -0
  8. package/opencode/agent/the-analyst/project-coordination.md +81 -0
  9. package/opencode/agent/the-analyst/requirements-analysis.md +77 -0
  10. package/opencode/agent/the-architect/compatibility-review.md +138 -0
  11. package/opencode/agent/the-architect/complexity-review.md +137 -0
  12. package/opencode/agent/the-architect/quality-review.md +67 -0
  13. package/opencode/agent/the-architect/security-review.md +127 -0
  14. package/opencode/agent/the-architect/system-architecture.md +119 -0
  15. package/opencode/agent/the-architect/system-documentation.md +83 -0
  16. package/opencode/agent/the-architect/technology-research.md +85 -0
  17. package/opencode/agent/the-chief.md +79 -0
  18. package/opencode/agent/the-designer/accessibility-implementation.md +101 -0
  19. package/opencode/agent/the-designer/design-foundation.md +74 -0
  20. package/opencode/agent/the-designer/interaction-architecture.md +75 -0
  21. package/opencode/agent/the-designer/user-research.md +70 -0
  22. package/opencode/agent/the-meta-agent.md +155 -0
  23. package/opencode/agent/the-platform-engineer/ci-cd-pipelines.md +109 -0
  24. package/opencode/agent/the-platform-engineer/containerization.md +106 -0
  25. package/opencode/agent/the-platform-engineer/data-architecture.md +81 -0
  26. package/opencode/agent/the-platform-engineer/dependency-review.md +144 -0
  27. package/opencode/agent/the-platform-engineer/deployment-automation.md +81 -0
  28. package/opencode/agent/the-platform-engineer/infrastructure-as-code.md +107 -0
  29. package/opencode/agent/the-platform-engineer/performance-tuning.md +82 -0
  30. package/opencode/agent/the-platform-engineer/pipeline-engineering.md +81 -0
  31. package/opencode/agent/the-platform-engineer/production-monitoring.md +105 -0
  32. package/opencode/agent/the-qa-engineer/exploratory-testing.md +66 -0
  33. package/opencode/agent/the-qa-engineer/performance-testing.md +81 -0
  34. package/opencode/agent/the-qa-engineer/quality-assurance.md +77 -0
  35. package/opencode/agent/the-qa-engineer/test-execution.md +66 -0
  36. package/opencode/agent/the-software-engineer/api-development.md +78 -0
  37. package/opencode/agent/the-software-engineer/component-development.md +79 -0
  38. package/opencode/agent/the-software-engineer/concurrency-review.md +141 -0
  39. package/opencode/agent/the-software-engineer/domain-modeling.md +66 -0
  40. package/opencode/agent/the-software-engineer/performance-optimization.md +113 -0
  41. package/opencode/command/analyze.md +149 -0
  42. package/opencode/command/constitution.md +178 -0
  43. package/opencode/command/debug.md +194 -0
  44. package/opencode/command/document.md +178 -0
  45. package/opencode/command/implement.md +225 -0
  46. package/opencode/command/refactor.md +207 -0
  47. package/opencode/command/review.md +229 -0
  48. package/opencode/command/simplify.md +267 -0
  49. package/opencode/command/specify.md +191 -0
  50. package/opencode/command/validate.md +224 -0
  51. package/opencode/skill/accessibility-design/SKILL.md +566 -0
  52. package/opencode/skill/accessibility-design/checklists/wcag-checklist.md +435 -0
  53. package/opencode/skill/agent-coordination/SKILL.md +224 -0
  54. package/opencode/skill/api-contract-design/SKILL.md +550 -0
  55. package/opencode/skill/api-contract-design/templates/graphql-schema-template.md +818 -0
  56. package/opencode/skill/api-contract-design/templates/rest-api-template.md +417 -0
  57. package/opencode/skill/architecture-design/SKILL.md +160 -0
  58. package/opencode/skill/architecture-design/examples/architecture-examples.md +170 -0
  59. package/opencode/skill/architecture-design/template.md +749 -0
  60. package/opencode/skill/architecture-design/validation.md +99 -0
  61. package/opencode/skill/architecture-selection/SKILL.md +522 -0
  62. package/opencode/skill/architecture-selection/examples/adrs/001-example-adr.md +71 -0
  63. package/opencode/skill/architecture-selection/examples/architecture-patterns.md +239 -0
  64. package/opencode/skill/bug-diagnosis/SKILL.md +235 -0
  65. package/opencode/skill/code-quality-review/SKILL.md +337 -0
  66. package/opencode/skill/code-quality-review/examples/anti-patterns.md +629 -0
  67. package/opencode/skill/code-quality-review/reference.md +322 -0
  68. package/opencode/skill/code-review/SKILL.md +363 -0
  69. package/opencode/skill/code-review/reference.md +450 -0
  70. package/opencode/skill/codebase-analysis/SKILL.md +139 -0
  71. package/opencode/skill/codebase-navigation/SKILL.md +227 -0
  72. package/opencode/skill/codebase-navigation/examples/exploration-patterns.md +263 -0
  73. package/opencode/skill/coding-conventions/SKILL.md +178 -0
  74. package/opencode/skill/coding-conventions/checklists/accessibility-checklist.md +176 -0
  75. package/opencode/skill/coding-conventions/checklists/performance-checklist.md +154 -0
  76. package/opencode/skill/coding-conventions/checklists/security-checklist.md +127 -0
  77. package/opencode/skill/constitution-validation/SKILL.md +315 -0
  78. package/opencode/skill/constitution-validation/examples/CONSTITUTION.md +202 -0
  79. package/opencode/skill/constitution-validation/reference/rule-patterns.md +328 -0
  80. package/opencode/skill/constitution-validation/template.md +115 -0
  81. package/opencode/skill/context-preservation/SKILL.md +445 -0
  82. package/opencode/skill/data-modeling/SKILL.md +385 -0
  83. package/opencode/skill/data-modeling/templates/schema-design-template.md +268 -0
  84. package/opencode/skill/deployment-pipeline-design/SKILL.md +579 -0
  85. package/opencode/skill/deployment-pipeline-design/templates/pipeline-template.md +633 -0
  86. package/opencode/skill/documentation-extraction/SKILL.md +259 -0
  87. package/opencode/skill/documentation-sync/SKILL.md +431 -0
  88. package/opencode/skill/domain-driven-design/SKILL.md +509 -0
  89. package/opencode/skill/domain-driven-design/examples/ddd-patterns.md +688 -0
  90. package/opencode/skill/domain-driven-design/reference.md +465 -0
  91. package/opencode/skill/drift-detection/SKILL.md +383 -0
  92. package/opencode/skill/drift-detection/reference.md +340 -0
  93. package/opencode/skill/error-recovery/SKILL.md +162 -0
  94. package/opencode/skill/error-recovery/examples/error-patterns.md +484 -0
  95. package/opencode/skill/feature-prioritization/SKILL.md +419 -0
  96. package/opencode/skill/feature-prioritization/examples/rice-template.md +139 -0
  97. package/opencode/skill/feature-prioritization/reference.md +256 -0
  98. package/opencode/skill/git-workflow/SKILL.md +453 -0
  99. package/opencode/skill/implementation-planning/SKILL.md +215 -0
  100. package/opencode/skill/implementation-planning/examples/phase-examples.md +217 -0
  101. package/opencode/skill/implementation-planning/template.md +220 -0
  102. package/opencode/skill/implementation-planning/validation.md +88 -0
  103. package/opencode/skill/implementation-verification/SKILL.md +272 -0
  104. package/opencode/skill/knowledge-capture/SKILL.md +265 -0
  105. package/opencode/skill/knowledge-capture/reference/knowledge-capture.md +402 -0
  106. package/opencode/skill/knowledge-capture/reference.md +444 -0
  107. package/opencode/skill/knowledge-capture/templates/domain-template.md +325 -0
  108. package/opencode/skill/knowledge-capture/templates/interface-template.md +255 -0
  109. package/opencode/skill/knowledge-capture/templates/pattern-template.md +144 -0
  110. package/opencode/skill/observability-design/SKILL.md +291 -0
  111. package/opencode/skill/observability-design/references/monitoring-patterns.md +461 -0
  112. package/opencode/skill/pattern-detection/SKILL.md +171 -0
  113. package/opencode/skill/pattern-detection/examples/common-patterns.md +359 -0
  114. package/opencode/skill/performance-analysis/SKILL.md +266 -0
  115. package/opencode/skill/performance-analysis/references/profiling-tools.md +499 -0
  116. package/opencode/skill/requirements-analysis/SKILL.md +139 -0
  117. package/opencode/skill/requirements-analysis/examples/good-prd.md +66 -0
  118. package/opencode/skill/requirements-analysis/template.md +177 -0
  119. package/opencode/skill/requirements-analysis/validation.md +69 -0
  120. package/opencode/skill/requirements-elicitation/SKILL.md +518 -0
  121. package/opencode/skill/requirements-elicitation/examples/interview-questions.md +226 -0
  122. package/opencode/skill/requirements-elicitation/examples/user-stories.md +414 -0
  123. package/opencode/skill/safe-refactoring/SKILL.md +312 -0
  124. package/opencode/skill/safe-refactoring/reference/code-smells.md +347 -0
  125. package/opencode/skill/security-assessment/SKILL.md +421 -0
  126. package/opencode/skill/security-assessment/checklists/security-review-checklist.md +285 -0
  127. package/opencode/skill/specification-management/SKILL.md +143 -0
  128. package/opencode/skill/specification-management/readme-template.md +32 -0
  129. package/opencode/skill/specification-management/reference.md +115 -0
  130. package/opencode/skill/specification-management/spec.py +229 -0
  131. package/opencode/skill/specification-validation/SKILL.md +397 -0
  132. package/opencode/skill/specification-validation/reference/3cs-framework.md +306 -0
  133. package/opencode/skill/specification-validation/reference/ambiguity-detection.md +132 -0
  134. package/opencode/skill/specification-validation/reference/constitution-validation.md +301 -0
  135. package/opencode/skill/specification-validation/reference/drift-detection.md +383 -0
  136. package/opencode/skill/task-delegation/SKILL.md +607 -0
  137. package/opencode/skill/task-delegation/examples/file-coordination.md +495 -0
  138. package/opencode/skill/task-delegation/examples/parallel-research.md +337 -0
  139. package/opencode/skill/task-delegation/examples/sequential-build.md +504 -0
  140. package/opencode/skill/task-delegation/reference.md +825 -0
  141. package/opencode/skill/tech-stack-detection/SKILL.md +89 -0
  142. package/opencode/skill/tech-stack-detection/references/framework-signatures.md +598 -0
  143. package/opencode/skill/technical-writing/SKILL.md +190 -0
  144. package/opencode/skill/technical-writing/templates/adr-template.md +205 -0
  145. package/opencode/skill/technical-writing/templates/system-doc-template.md +380 -0
  146. package/opencode/skill/test-design/SKILL.md +464 -0
  147. package/opencode/skill/test-design/examples/test-pyramid.md +724 -0
  148. package/opencode/skill/testing/SKILL.md +213 -0
  149. package/opencode/skill/testing/examples/test-pyramid.md +724 -0
  150. package/opencode/skill/user-insight-synthesis/SKILL.md +576 -0
  151. package/opencode/skill/user-insight-synthesis/templates/research-plan-template.md +217 -0
  152. package/opencode/skill/user-research/SKILL.md +508 -0
  153. package/opencode/skill/user-research/examples/interview-questions.md +265 -0
  154. package/opencode/skill/user-research/examples/personas.md +267 -0
  155. package/opencode/skill/vibe-security/SKILL.md +654 -0
  156. package/package.json +45 -0
@@ -0,0 +1,629 @@
1
+ # Anti-Pattern Catalog
2
+
3
+ Detailed examples of common code anti-patterns: what they look like in practice, why they cause problems, and how to refactor them. Use this when a SKILL.md table entry needs deeper analysis or when explaining a finding to a developer.
4
+
5
+ ---
6
+
7
+ ## God Object
8
+
9
+ ### What It Looks Like
10
+
11
+ A class that accumulates responsibilities over time until it knows too much and does too much. Often starts as a reasonable service class and grows through expedient additions.
12
+
13
+ ```
14
+ UserManager:
15
+ createUser()
16
+ updateUser()
17
+ deleteUser()
18
+ getUserById()
19
+ getUserByEmail()
20
+ sendWelcomeEmail()
21
+ sendPasswordResetEmail()
22
+ validateUserPermissions()
23
+ generateAuthToken()
24
+ revokeAuthToken()
25
+ refreshAuthToken()
26
+ logUserActivity()
27
+ getUserActivityReport()
28
+ exportUserDataToCsv()
29
+ importUsersFromCsv()
30
+ syncUsersWithLdap()
31
+ ```
32
+
33
+ ### Why It Is Problematic
34
+
35
+ - Every team member who touches users touches this file — merge conflicts are constant
36
+ - Changing authentication logic risks breaking CSV export
37
+ - The class is untestable in isolation because it depends on email, database, LDAP, and auth systems simultaneously
38
+ - New developers cannot infer what "the system does with users" because the answer is "everything"
39
+ - The class has no single reason to change — it changes for every user-related requirement
40
+
41
+ ### How to Refactor
42
+
43
+ Identify natural responsibility clusters. Each cluster becomes a focused class:
44
+
45
+ ```
46
+ UserRepository — persistence only (create, update, delete, findBy*)
47
+ UserEmailService — email communication (sendWelcome, sendPasswordReset)
48
+ AuthTokenService — token lifecycle (generate, revoke, refresh)
49
+ PermissionService — authorization checks (validatePermissions)
50
+ UserActivityLogger — audit trail (logActivity, getReport)
51
+ UserImportExport — CSV operations (import, export)
52
+ LdapSyncService — directory sync (syncUsers)
53
+ ```
54
+
55
+ Each class has a single reason to change. `UserRepository` changes when the persistence layer changes. `UserEmailService` changes when email templates change. They can be tested independently.
56
+
57
+ Refactor incrementally — extract one cluster at a time, run tests between each extraction.
58
+
59
+ ---
60
+
61
+ ## Spaghetti Code
62
+
63
+ ### What It Looks Like
64
+
65
+ Control flow that jumps unpredictably through conditionals, early returns, exceptions used for control flow, and functions that call back into their callers. The execution path is difficult to trace.
66
+
67
+ ```python
68
+ def process_payment(order, user, payment_method):
69
+ if payment_method == 'card':
70
+ if not user.has_saved_card:
71
+ if order.total > 0:
72
+ try:
73
+ result = stripe.charge(order.total, user.card_token)
74
+ if result.status == 'succeeded':
75
+ order.status = 'paid'
76
+ send_receipt(user, order)
77
+ if user.loyalty_enabled:
78
+ points = calculate_points(order.total)
79
+ if points > 0:
80
+ user.loyalty_points += points
81
+ if user.loyalty_points >= 1000:
82
+ send_loyalty_reward(user)
83
+ else:
84
+ order.status = 'failed'
85
+ log_failure(result)
86
+ if user.retry_enabled:
87
+ retry_payment(order, user)
88
+ except stripe.CardError as e:
89
+ handle_card_error(e, order, user)
90
+ except Exception as e:
91
+ raise PaymentException("unexpected") from e
92
+ else:
93
+ raise ValueError("no saved card")
94
+ elif payment_method == 'invoice':
95
+ # 40 more lines
96
+ ```
97
+
98
+ ### Why It Is Problematic
99
+
100
+ - Adding a new payment method requires understanding the entire existing structure
101
+ - The loyalty points logic is buried inside payment processing — impossible to test in isolation
102
+ - Cyclomatic complexity is high (10+ paths) — almost certainly under-tested
103
+ - When a bug is found in the card error path, the fix risks destabilizing the invoice path
104
+ - Reading the function requires holding many open conditionals in working memory
105
+
106
+ ### How to Refactor
107
+
108
+ Separate concerns. Each logical step becomes a named function at the same abstraction level:
109
+
110
+ ```python
111
+ def process_payment(order, user, payment_method):
112
+ validate_payment_prerequisites(order, user, payment_method)
113
+ result = charge_payment(order, user, payment_method)
114
+ apply_payment_result(result, order, user)
115
+
116
+ def validate_payment_prerequisites(order, user, payment_method):
117
+ if payment_method == 'card' and not user.has_saved_card:
118
+ raise MissingPaymentMethodError("User has no saved card on file")
119
+
120
+ def charge_payment(order, user, payment_method):
121
+ if payment_method == 'card':
122
+ return _charge_card(order, user)
123
+ if payment_method == 'invoice':
124
+ return _issue_invoice(order, user)
125
+ raise UnsupportedPaymentMethodError(payment_method)
126
+
127
+ def apply_payment_result(result, order, user):
128
+ order.status = 'paid' if result.succeeded else 'failed'
129
+ if result.succeeded:
130
+ _handle_successful_payment(order, user)
131
+ else:
132
+ _handle_failed_payment(result, order, user)
133
+
134
+ def _handle_successful_payment(order, user):
135
+ send_receipt(user, order)
136
+ _award_loyalty_points_if_eligible(user, order.total)
137
+
138
+ def _award_loyalty_points_if_eligible(user, amount):
139
+ if not user.loyalty_enabled:
140
+ return
141
+ points = calculate_points(amount)
142
+ user.loyalty_points += points
143
+ if user.loyalty_points >= LOYALTY_REWARD_THRESHOLD:
144
+ send_loyalty_reward(user)
145
+ ```
146
+
147
+ Each function now has a single responsibility and a complexity of 1-3. The loyalty logic can be tested without processing a payment.
148
+
149
+ ---
150
+
151
+ ## Copy-Paste Programming
152
+
153
+ ### What It Looks Like
154
+
155
+ Identical or near-identical logic duplicated across the codebase. Often spotted by similar variable names, same comment blocks, or adjacent functions that differ only in the data they operate on.
156
+
157
+ ```javascript
158
+ function validateUserRegistration(data) {
159
+ const errors = [];
160
+ if (!data.email || !data.email.includes('@')) {
161
+ errors.push('Email is invalid');
162
+ }
163
+ if (!data.password || data.password.length < 8) {
164
+ errors.push('Password must be at least 8 characters');
165
+ }
166
+ if (!data.name || data.name.trim().length === 0) {
167
+ errors.push('Name is required');
168
+ }
169
+ return errors;
170
+ }
171
+
172
+ function validateUserUpdate(data) {
173
+ const errors = [];
174
+ if (data.email && !data.email.includes('@')) {
175
+ errors.push('Email is invalid');
176
+ }
177
+ if (data.password && data.password.length < 8) {
178
+ errors.push('Password must be at least 8 characters');
179
+ }
180
+ if (data.name !== undefined && data.name.trim().length === 0) {
181
+ errors.push('Name is required');
182
+ }
183
+ return errors;
184
+ }
185
+ ```
186
+
187
+ ### Why It Is Problematic
188
+
189
+ - A bug fix in one copy must be found and applied to all copies — and copies are often not found
190
+ - The two functions have slightly different semantics (required vs optional fields) but the duplication obscures the intentional difference from the accidental duplication
191
+ - When the email validation rule changes (e.g., to use a proper regex), it will be changed in one place and missed in the other
192
+ - The next developer will copy-paste again rather than extract, deepening the problem
193
+
194
+ ### How to Refactor
195
+
196
+ Extract the shared validation rules. Parameterize what varies:
197
+
198
+ ```javascript
199
+ const emailRule = (required = true) => ({
200
+ validate: (value) => !required && !value ? null : (value?.includes('@') ? null : 'Email is invalid'),
201
+ });
202
+
203
+ const passwordRule = (required = true) => ({
204
+ validate: (value) => !required && !value ? null : (value?.length >= 8 ? null : 'Password must be at least 8 characters'),
205
+ });
206
+
207
+ const nameRule = (required = true) => ({
208
+ validate: (value) => !required && value === undefined ? null : (value?.trim().length > 0 ? null : 'Name is required'),
209
+ });
210
+
211
+ function validate(data, rules) {
212
+ return Object.entries(rules)
213
+ .map(([field, rule]) => rule.validate(data[field]))
214
+ .filter(Boolean);
215
+ }
216
+
217
+ const registrationRules = {
218
+ email: emailRule(true),
219
+ password: passwordRule(true),
220
+ name: nameRule(true),
221
+ };
222
+
223
+ const updateRules = {
224
+ email: emailRule(false),
225
+ password: passwordRule(false),
226
+ name: nameRule(false),
227
+ };
228
+
229
+ function validateUserRegistration(data) {
230
+ return validate(data, registrationRules);
231
+ }
232
+
233
+ function validateUserUpdate(data) {
234
+ return validate(data, updateRules);
235
+ }
236
+ ```
237
+
238
+ The intentional difference (required vs optional) is now explicit. The validation logic exists once.
239
+
240
+ ---
241
+
242
+ ## Premature Optimization
243
+
244
+ ### What It Looks Like
245
+
246
+ Complex code that sacrifices readability for performance before there is evidence of a performance problem. Often includes caching for operations that run once, bit manipulation for clarity, or micro-optimizations that modern compilers perform automatically.
247
+
248
+ ```python
249
+ # Processing a list of ~50 user records from a form submission
250
+ def process_users(users):
251
+ # "Optimized" with manual hash map to avoid O(n) lookup
252
+ user_index = {}
253
+ for i in range(len(users)):
254
+ user_index[users[i]['id']] = i
255
+
256
+ # Bit manipulation instead of modulo
257
+ batch_size = 16
258
+ batch_mask = batch_size - 1
259
+
260
+ results = [None] * len(users) # Pre-allocated for "performance"
261
+ batch_buffer = []
262
+
263
+ for i in range(len(users)):
264
+ batch_buffer.append(users[i])
265
+ if (i & batch_mask) == batch_mask or i == len(users) - 1:
266
+ # Process batch
267
+ for user in batch_buffer:
268
+ idx = user_index[user['id']]
269
+ results[idx] = transform_user(user)
270
+ batch_buffer = []
271
+
272
+ return results
273
+ ```
274
+
275
+ ### Why It Is Problematic
276
+
277
+ - The code processes at most 50 items — the "optimization" provides zero measurable benefit at this scale
278
+ - A reviewer must understand bit masking, index remapping, and manual batching to verify correctness
279
+ - The complexity introduces a real bug opportunity (off-by-one in batch boundary detection)
280
+ - Performance assumptions baked into the code are not documented — the next developer does not know why this is complex
281
+ - If the actual bottleneck is the `transform_user` function or a database call, this optimization is in the wrong place
282
+
283
+ ### How to Refactor
284
+
285
+ Write the simple version. Measure if performance becomes an issue. Optimize the measured bottleneck:
286
+
287
+ ```python
288
+ def process_users(users):
289
+ return [transform_user(user) for user in users]
290
+ ```
291
+
292
+ If profiling reveals this is genuinely slow at real-world scale, the optimization is warranted. Document it:
293
+
294
+ ```python
295
+ def process_users(users):
296
+ # Process in batches of 500 to stay within API rate limits.
297
+ # Batch size determined by load testing at 10k users/minute.
298
+ # See: docs/adr/0012-user-batch-processing.md
299
+ BATCH_SIZE = 500
300
+ results = []
301
+ for batch in chunked(users, BATCH_SIZE):
302
+ results.extend(_process_batch(batch))
303
+ return results
304
+ ```
305
+
306
+ ---
307
+
308
+ ## Magic Numbers
309
+
310
+ ### What It Looks Like
311
+
312
+ Numeric (or string) literals embedded in logic without explanation of what they represent or why they have that value.
313
+
314
+ ```javascript
315
+ function calculateShipping(orderTotal, destinationCountry) {
316
+ if (destinationCountry === 'US') {
317
+ if (orderTotal >= 75) {
318
+ return 0;
319
+ }
320
+ return 8.99;
321
+ }
322
+
323
+ if (orderTotal >= 150) {
324
+ return 0;
325
+ }
326
+
327
+ if (['CA', 'MX'].includes(destinationCountry)) {
328
+ return 15.99;
329
+ }
330
+
331
+ return 29.99;
332
+ }
333
+
334
+ function calculateLoyaltyPoints(orderTotal) {
335
+ return Math.floor(orderTotal * 1.5);
336
+ }
337
+
338
+ setTimeout(retryPayment, 30000);
339
+ ```
340
+
341
+ ### Why It Is Problematic
342
+
343
+ - `75` and `150` appear to be free shipping thresholds — but a reader cannot confirm this without business documentation
344
+ - When marketing changes the domestic free shipping threshold from $75 to $100, the developer must find every `75` in the codebase (risky with grep)
345
+ - `1.5` in loyalty points has no unit — is it 1.5 points per dollar? Per cent? The calculation is opaque
346
+ - `30000` is milliseconds — readers must know this and convert mentally
347
+ - Magic numbers in tests are particularly dangerous: they assert specific values without explaining why those values are expected
348
+
349
+ ### How to Refactor
350
+
351
+ Name every value that has domain meaning:
352
+
353
+ ```javascript
354
+ const FREE_SHIPPING_THRESHOLD_DOMESTIC = 75.00; // USD
355
+ const FREE_SHIPPING_THRESHOLD_INTERNATIONAL = 150.00; // USD
356
+ const SHIPPING_COST_DOMESTIC = 8.99;
357
+ const SHIPPING_COST_NORTH_AMERICA = 15.99;
358
+ const SHIPPING_COST_INTERNATIONAL = 29.99;
359
+ const NORTH_AMERICA_COUNTRIES = ['CA', 'MX'];
360
+
361
+ const LOYALTY_POINTS_PER_DOLLAR = 1.5;
362
+
363
+ const PAYMENT_RETRY_DELAY_MS = 30_000; // 30 seconds
364
+
365
+ function calculateShipping(orderTotal, destinationCountry) {
366
+ if (destinationCountry === 'US') {
367
+ return orderTotal >= FREE_SHIPPING_THRESHOLD_DOMESTIC ? 0 : SHIPPING_COST_DOMESTIC;
368
+ }
369
+ if (orderTotal >= FREE_SHIPPING_THRESHOLD_INTERNATIONAL) {
370
+ return 0;
371
+ }
372
+ return NORTH_AMERICA_COUNTRIES.includes(destinationCountry)
373
+ ? SHIPPING_COST_NORTH_AMERICA
374
+ : SHIPPING_COST_INTERNATIONAL;
375
+ }
376
+
377
+ function calculateLoyaltyPoints(orderTotal) {
378
+ return Math.floor(orderTotal * LOYALTY_POINTS_PER_DOLLAR);
379
+ }
380
+
381
+ setTimeout(retryPayment, PAYMENT_RETRY_DELAY_MS);
382
+ ```
383
+
384
+ Constants that change together (e.g., all shipping thresholds) should live in a single configuration file — not scattered across the codebase.
385
+
386
+ ---
387
+
388
+ ## Deep Nesting
389
+
390
+ ### What It Looks Like
391
+
392
+ Control flow that indents further and further right, making it difficult to track which conditions govern a given block of code.
393
+
394
+ ```typescript
395
+ function processOrderItem(order, item, user) {
396
+ if (order !== null) {
397
+ if (order.status === 'active') {
398
+ if (item !== null) {
399
+ if (item.inStock) {
400
+ if (user !== null) {
401
+ if (user.isVerified) {
402
+ if (item.price <= user.creditLimit) {
403
+ // The actual logic — buried at level 7
404
+ addItemToOrder(order, item);
405
+ deductFromCreditLimit(user, item.price);
406
+ updateInventory(item);
407
+ return { success: true };
408
+ } else {
409
+ return { success: false, reason: 'insufficient_credit' };
410
+ }
411
+ } else {
412
+ return { success: false, reason: 'unverified_user' };
413
+ }
414
+ } else {
415
+ return { success: false, reason: 'no_user' };
416
+ }
417
+ } else {
418
+ return { success: false, reason: 'out_of_stock' };
419
+ }
420
+ } else {
421
+ return { success: false, reason: 'no_item' };
422
+ }
423
+ } else {
424
+ return { success: false, reason: 'inactive_order' };
425
+ }
426
+ } else {
427
+ return { success: false, reason: 'no_order' };
428
+ }
429
+ }
430
+ ```
431
+
432
+ ### Why It Is Problematic
433
+
434
+ - The happy path (the actual logic) is invisible — buried at the deepest level of nesting
435
+ - Each closing brace must be mentally matched to its opening — readers count braces
436
+ - Adding a new guard condition requires restructuring the entire block
437
+ - The function is 30+ lines for logic that occupies 3 lines
438
+ - Error handling is scattered — the `insufficient_credit` case and the `no_order` case look structurally identical but carry completely different meaning
439
+
440
+ ### How to Refactor
441
+
442
+ Invert conditions with guard clauses. Return early on invalid cases. The happy path stays at the left margin:
443
+
444
+ ```typescript
445
+ function processOrderItem(order, item, user) {
446
+ if (order === null) return { success: false, reason: 'no_order' };
447
+ if (order.status !== 'active') return { success: false, reason: 'inactive_order' };
448
+ if (item === null) return { success: false, reason: 'no_item' };
449
+ if (!item.inStock) return { success: false, reason: 'out_of_stock' };
450
+ if (user === null) return { success: false, reason: 'no_user' };
451
+ if (!user.isVerified) return { success: false, reason: 'unverified_user' };
452
+ if (item.price > user.creditLimit) return { success: false, reason: 'insufficient_credit' };
453
+
454
+ addItemToOrder(order, item);
455
+ deductFromCreditLimit(user, item.price);
456
+ updateInventory(item);
457
+
458
+ return { success: true };
459
+ }
460
+ ```
461
+
462
+ Same logic, same behavior, zero nesting. The happy path is immediately visible. New guard conditions are added as a new line at the top, not a new level of indentation.
463
+
464
+ ---
465
+
466
+ ## Feature Envy
467
+
468
+ ### What It Looks Like
469
+
470
+ A method that is more interested in another class's data than its own. It reaches into another object repeatedly to do work that belongs to that other object.
471
+
472
+ ```python
473
+ class Order:
474
+ def __init__(self, items, customer):
475
+ self.items = items
476
+ self.customer = customer
477
+
478
+ def calculate_discount(self):
479
+ # This method "envies" the Customer class
480
+ if self.customer.membership_tier == 'gold':
481
+ if self.customer.years_as_customer >= 5:
482
+ return 0.20
483
+ return 0.15
484
+ if self.customer.membership_tier == 'silver':
485
+ if self.customer.years_as_customer >= 3:
486
+ return 0.10
487
+ return 0.05
488
+ if self.customer.total_lifetime_spend > 10000:
489
+ return 0.03
490
+ return 0
491
+
492
+ class InvoiceGenerator:
493
+ def format_shipping_address(self, order):
494
+ # This method knows too much about Customer's address structure
495
+ customer = order.customer
496
+ line1 = customer.address.street_number + ' ' + customer.address.street_name
497
+ if customer.address.apartment:
498
+ line1 += ', Apt ' + customer.address.apartment
499
+ city_line = customer.address.city + ', ' + customer.address.state
500
+ if customer.address.country != 'US':
501
+ city_line += ', ' + customer.address.country
502
+ return line1 + '\n' + city_line + '\n' + customer.address.postal_code
503
+ ```
504
+
505
+ ### Why It Is Problematic
506
+
507
+ - Discount rules live in `Order` but are entirely about `Customer` properties — when discount rules change, `Order` changes
508
+ - If `Customer.address` gains a new field, `InvoiceGenerator` must be updated — a coupling that should not exist
509
+ - The knowledge about how to format an address is now in two places: the generator and wherever else code accesses these fields
510
+ - Testing `calculate_discount` requires constructing full `Customer` objects instead of testing the discount logic alone
511
+
512
+ ### How to Refactor
513
+
514
+ Move the method to the class whose data it uses:
515
+
516
+ ```python
517
+ class Customer:
518
+ def discount_rate(self):
519
+ if self.membership_tier == 'gold':
520
+ return 0.20 if self.years_as_customer >= 5 else 0.15
521
+ if self.membership_tier == 'silver':
522
+ return 0.10 if self.years_as_customer >= 3 else 0.05
523
+ if self.total_lifetime_spend > 10000:
524
+ return 0.03
525
+ return 0
526
+
527
+ def formatted_shipping_address(self):
528
+ return self.address.formatted()
529
+
530
+ class Address:
531
+ def formatted(self):
532
+ line1 = f"{self.street_number} {self.street_name}"
533
+ if self.apartment:
534
+ line1 += f", Apt {self.apartment}"
535
+ city_line = f"{self.city}, {self.state}"
536
+ if self.country != 'US':
537
+ city_line += f", {self.country}"
538
+ return f"{line1}\n{city_line}\n{self.postal_code}"
539
+
540
+ class Order:
541
+ def calculate_discount(self):
542
+ return self.customer.discount_rate()
543
+
544
+ class InvoiceGenerator:
545
+ def format_shipping_address(self, order):
546
+ return order.customer.formatted_shipping_address()
547
+ ```
548
+
549
+ Each class owns its own data and the logic that operates on it. Address formatting is in `Address`. Discount calculation is in `Customer`.
550
+
551
+ ---
552
+
553
+ ## Shotgun Surgery
554
+
555
+ ### What It Looks Like
556
+
557
+ A single conceptual change requires edits to many files. Common when a concern is spread across the codebase rather than encapsulated.
558
+
559
+ ```
560
+ Scenario: "Add a new user role: 'moderator'"
561
+
562
+ Files that must change:
563
+ src/auth/permissions.ts — add role to enum
564
+ src/auth/guards/role.guard.ts — add role to allowed list
565
+ src/middleware/authorize.ts — handle new role in switch
566
+ src/db/migrations/... — add role to DB enum
567
+ src/db/seeds/roles.ts — add seed record
568
+ src/api/users/users.controller.ts — add role to DTO validation
569
+ src/api/users/users.service.ts — add role-specific logic
570
+ src/frontend/components/RoleBadge.tsx — add display case
571
+ src/frontend/hooks/usePermissions.ts — add permission checks
572
+ src/tests/fixtures/users.ts — update test fixtures
573
+ src/tests/auth/permissions.test.ts — add test cases
574
+ docs/api/users.md — update API documentation
575
+ ```
576
+
577
+ ### Why It Is Problematic
578
+
579
+ - Adding a role takes hours instead of minutes — not because of complexity, but because of coordination overhead
580
+ - Each file change is a merge conflict opportunity
581
+ - It is easy to miss one file — the bug only appears when that specific code path is triggered
582
+ - The codebase gives no indication that these files are related — a developer must know to look
583
+ - Reviewing the change is difficult: the reviewer must trace the conceptual change across 12 files
584
+
585
+ ### How to Refactor
586
+
587
+ Encapsulate the concern. A role definition should exist in one place:
588
+
589
+ ```typescript
590
+ // src/domain/roles/role.ts
591
+ export const Role = {
592
+ ADMIN: 'admin',
593
+ MODERATOR: 'moderator',
594
+ USER: 'user',
595
+ } as const;
596
+
597
+ export type RoleType = typeof Role[keyof typeof Role];
598
+
599
+ export const RolePermissions: Record<RoleType, Permission[]> = {
600
+ [Role.ADMIN]: [Permission.READ, Permission.WRITE, Permission.DELETE, Permission.MODERATE],
601
+ [Role.MODERATOR]: [Permission.READ, Permission.WRITE, Permission.MODERATE],
602
+ [Role.USER]: [Permission.READ, Permission.WRITE],
603
+ };
604
+
605
+ export const RoleDisplayName: Record<RoleType, string> = {
606
+ [Role.ADMIN]: 'Administrator',
607
+ [Role.MODERATOR]: 'Moderator',
608
+ [Role.USER]: 'User',
609
+ };
610
+ ```
611
+
612
+ Now adding a role is a single file change. Everything that depends on role definitions imports from this one location. The migration and tests still change, but the application logic does not scatter.
613
+
614
+ The test for whether Shotgun Surgery is resolved: can a developer make the conceptual change (add a role) by editing one logical location?
615
+
616
+ ---
617
+
618
+ ## Pattern Summary
619
+
620
+ | Anti-Pattern | Primary Dimension | Key Signal | First Refactoring Step |
621
+ |--------------|-------------------|------------|------------------------|
622
+ | God Object | Design | Class with 10+ methods spanning unrelated concerns | List responsibilities, find natural clusters |
623
+ | Spaghetti Code | Readability | Cannot trace execution path without diagramming | Identify the happy path, separate guard clauses |
624
+ | Copy-Paste Programming | Maintainability | Two functions that differ in one parameter | Find what varies, extract shared logic |
625
+ | Premature Optimization | Readability | Complexity added before measuring performance | Write the obvious version, delete the complex one |
626
+ | Magic Numbers | Readability | Literal values in logic with no named constant | Name every value that has domain meaning |
627
+ | Deep Nesting | Readability | Happy path is at indent level 4+ | Invert conditions, return early on failure |
628
+ | Feature Envy | Design | Method uses another class's data more than its own | Move method to the class whose data it uses |
629
+ | Shotgun Surgery | Maintainability | One concept requires N file changes | Find the concept, encapsulate it in one place |