@lenardangeloolajay/len-toolkit 1.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.
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/bin/cli.js +244 -0
- package/package.json +35 -0
- package/src/installer.js +236 -0
- package/templates/configs/.editorconfig +12 -0
- package/templates/configs/sample.gitignore +8 -0
- package/templates/docs/FEATURE.md +40 -0
- package/templates/docs/HANDOFF.md +39 -0
- package/templates/docs/IMPLEMENTATION_PLAN.md +47 -0
- package/templates/docs/SPEC_INDEX.md +12 -0
- package/templates/docs/VERIFICATION.md +14 -0
- package/templates/docs/product/ARCHITECTURE.md +22 -0
- package/templates/docs/product/CONSTRAINTS.md +22 -0
- package/templates/docs/product/DATA_MODEL.md +22 -0
- package/templates/docs/product/OVERVIEW.md +22 -0
- package/templates/examples/flutter-handoff/EXERCISES.md +16 -0
- package/templates/examples/flutter-handoff/HANDOFF.md +32 -0
- package/templates/examples/flutter-handoff/docs/SPEC_INDEX.md +19 -0
- package/templates/examples/flutter-handoff/docs/evidence/FEAT-001-verification.md +14 -0
- package/templates/examples/flutter-handoff/docs/features/FEAT-001-local-note.md +40 -0
- package/templates/examples/flutter-handoff/docs/plans/FEAT-001-implementation.md +43 -0
- package/templates/examples/flutter-handoff/docs/product/ARCHITECTURE.md +24 -0
- package/templates/examples/flutter-handoff/docs/product/CONSTRAINTS.md +13 -0
- package/templates/examples/flutter-handoff/docs/product/DATA_MODEL.md +20 -0
- package/templates/examples/flutter-handoff/docs/product/OVERVIEW.md +26 -0
- package/templates/rules/.cursorrules +7 -0
- package/templates/rules/AGENTS.md +61 -0
- package/templates/rules/GEMINI.md +7 -0
- package/templates/skills/a-philosophy-of-software-design/SKILL.md +11 -0
- package/templates/skills/a-philosophy-of-software-design/a-philosophy-of-software-design.md +370 -0
- package/templates/skills/a-philosophy-of-software-design/a-philosophy-of-software-design.mini.md +46 -0
- package/templates/skills/a-philosophy-of-software-design/a-philosophy-of-software-design.nano.md +35 -0
- package/templates/skills/clean-architecture/SKILL.md +11 -0
- package/templates/skills/clean-architecture/clean-architecture.md +515 -0
- package/templates/skills/clean-architecture/clean-architecture.mini.md +49 -0
- package/templates/skills/clean-architecture/clean-architecture.nano.md +36 -0
- package/templates/skills/clean-code/SKILL.md +11 -0
- package/templates/skills/clean-code/clean-code.md +297 -0
- package/templates/skills/clean-code/clean-code.mini.md +47 -0
- package/templates/skills/clean-code/clean-code.nano.md +32 -0
- package/templates/skills/code-complete/SKILL.md +11 -0
- package/templates/skills/code-complete/code-complete.md +354 -0
- package/templates/skills/code-complete/code-complete.mini.md +56 -0
- package/templates/skills/code-complete/code-complete.nano.md +41 -0
- package/templates/skills/council/SKILL.md +37 -0
- package/templates/skills/designing-data-intensive-applications/SKILL.md +11 -0
- package/templates/skills/designing-data-intensive-applications/designing-data-intensive-applications.md +393 -0
- package/templates/skills/designing-data-intensive-applications/designing-data-intensive-applications.mini.md +55 -0
- package/templates/skills/designing-data-intensive-applications/designing-data-intensive-applications.nano.md +34 -0
- package/templates/skills/domain-driven-design/SKILL.md +11 -0
- package/templates/skills/domain-driven-design/domain-driven-design.md +979 -0
- package/templates/skills/domain-driven-design/domain-driven-design.mini.md +48 -0
- package/templates/skills/domain-driven-design/domain-driven-design.nano.md +39 -0
- package/templates/skills/domain-driven-design-distilled/SKILL.md +11 -0
- package/templates/skills/domain-driven-design-distilled/domain-driven-design-distilled.md +317 -0
- package/templates/skills/domain-driven-design-distilled/domain-driven-design-distilled.mini.md +56 -0
- package/templates/skills/domain-driven-design-distilled/domain-driven-design-distilled.nano.md +41 -0
- package/templates/skills/implementation-plan/SKILL.md +42 -0
- package/templates/skills/implementing-domain-driven-design/SKILL.md +11 -0
- package/templates/skills/implementing-domain-driven-design/implementing-domain-driven-design.md +337 -0
- package/templates/skills/implementing-domain-driven-design/implementing-domain-driven-design.mini.md +57 -0
- package/templates/skills/implementing-domain-driven-design/implementing-domain-driven-design.nano.md +37 -0
- package/templates/skills/patterns-of-enterprise-application-architecture/SKILL.md +11 -0
- package/templates/skills/patterns-of-enterprise-application-architecture/patterns-of-enterprise-application-architecture.md +404 -0
- package/templates/skills/patterns-of-enterprise-application-architecture/patterns-of-enterprise-application-architecture.mini.md +54 -0
- package/templates/skills/patterns-of-enterprise-application-architecture/patterns-of-enterprise-application-architecture.nano.md +35 -0
- package/templates/skills/ponytail/SKILL.md +124 -0
- package/templates/skills/ponytail-audit/SKILL.md +45 -0
- package/templates/skills/ponytail-debt/SKILL.md +48 -0
- package/templates/skills/ponytail-gain/SKILL.md +17 -0
- package/templates/skills/ponytail-help/SKILL.md +42 -0
- package/templates/skills/ponytail-review/SKILL.md +61 -0
- package/templates/skills/refactoring/SKILL.md +11 -0
- package/templates/skills/refactoring/refactoring.md +433 -0
- package/templates/skills/refactoring/refactoring.mini.md +49 -0
- package/templates/skills/refactoring/refactoring.nano.md +37 -0
- package/templates/skills/refactoring-guru/SKILL.md +11 -0
- package/templates/skills/refactoring-guru/refactoring-guru.md +765 -0
- package/templates/skills/refactoring-guru/refactoring-guru.mini.md +64 -0
- package/templates/skills/refactoring-guru/refactoring-guru.nano.md +41 -0
- package/templates/skills/release-it/SKILL.md +11 -0
- package/templates/skills/release-it/release-it.md +382 -0
- package/templates/skills/release-it/release-it.mini.md +48 -0
- package/templates/skills/release-it/release-it.nano.md +38 -0
- package/templates/skills/security-audit/AI-AND-LLM.md +83 -0
- package/templates/skills/security-audit/ATTACK-CLASSES.md +130 -0
- package/templates/skills/security-audit/CLIENT-SIDE.md +83 -0
- package/templates/skills/security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
- package/templates/skills/security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
- package/templates/skills/security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
- package/templates/skills/security-audit/HUNTING.md +251 -0
- package/templates/skills/security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
- package/templates/skills/security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
- package/templates/skills/security-audit/RECONNAISSANCE.md +156 -0
- package/templates/skills/security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
- package/templates/skills/security-audit/SKILL.md +192 -0
- package/templates/skills/security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
- package/templates/skills/security-audit/VALIDATION-AND-REPORTING.md +186 -0
- package/templates/skills/security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
- package/templates/skills/security-audit/report-schema.json +461 -0
- package/templates/skills/security-audit/validate-coverage-ledger.cjs +872 -0
- package/templates/skills/security-audit/validate-coverage-ledger.test.cjs +740 -0
- package/templates/skills/security-audit/validate-findings.cjs +773 -0
- package/templates/skills/security-audit/validate-findings.test.cjs +652 -0
- package/templates/skills/spec/SKILL.md +55 -0
- package/templates/skills/the-pragmatic-programmer/SKILL.md +11 -0
- package/templates/skills/the-pragmatic-programmer/the-pragmatic-programmer.md +359 -0
- package/templates/skills/the-pragmatic-programmer/the-pragmatic-programmer.mini.md +65 -0
- package/templates/skills/the-pragmatic-programmer/the-pragmatic-programmer.nano.md +44 -0
- package/templates/skills/working-effectively-with-legacy-code/SKILL.md +11 -0
- package/templates/skills/working-effectively-with-legacy-code/working-effectively-with-legacy-code.md +371 -0
- package/templates/skills/working-effectively-with-legacy-code/working-effectively-with-legacy-code.mini.md +50 -0
- package/templates/skills/working-effectively-with-legacy-code/working-effectively-with-legacy-code.nano.md +35 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# OBEY Working Effectively with Legacy Code by Michael Feathers
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
This repository follows the discipline of **Working Effectively with Legacy Code** in the sense of Michael Feathers:
|
|
6
|
+
make risky existing code changeable by gaining understanding, creating seams, and establishing tests.
|
|
7
|
+
|
|
8
|
+
All code generation, edits, and reviews must optimize for:
|
|
9
|
+
- safe change in poorly understood code
|
|
10
|
+
- characterization before redesign
|
|
11
|
+
- breaking dependencies that block tests
|
|
12
|
+
- introducing seams
|
|
13
|
+
- reducing fear around modification
|
|
14
|
+
- incremental improvement instead of heroic rewrites
|
|
15
|
+
|
|
16
|
+
This file is a binding engineering policy: `MUST` is binding, `SHOULD` is a strong default, and `MUST NOT` is forbidden.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Primary Directive
|
|
21
|
+
|
|
22
|
+
Legacy code is code that is expensive to change safely.
|
|
23
|
+
In practice, the default assumption is:
|
|
24
|
+
|
|
25
|
+
**If a part of the code lacks trustworthy tests, treat it as legacy code.**
|
|
26
|
+
|
|
27
|
+
When modifying legacy code:
|
|
28
|
+
1. understand what it does now
|
|
29
|
+
2. protect that behavior with tests where possible
|
|
30
|
+
3. find or create a seam
|
|
31
|
+
4. break dependencies that prevent observation or isolation
|
|
32
|
+
5. make the requested change
|
|
33
|
+
6. leave the area more testable than before
|
|
34
|
+
|
|
35
|
+
Do not begin with a rewrite unless explicitly required.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Non-Negotiable Rules
|
|
40
|
+
|
|
41
|
+
1. **Do Not Rewrite by Reflex**
|
|
42
|
+
- Prefer targeted extraction and improvement.
|
|
43
|
+
- Rewrite only when explicitly requested or clearly safer than continued change.
|
|
44
|
+
|
|
45
|
+
2. **Characterize Before You Redesign**
|
|
46
|
+
- When current behavior is uncertain, capture it.
|
|
47
|
+
- Use characterization tests to document what the code does today, even if the behavior is ugly.
|
|
48
|
+
|
|
49
|
+
3. **Find or Create a Seam**
|
|
50
|
+
- A seam is a place where behavior can be changed without editing the surrounding code directly.
|
|
51
|
+
- Use seams to inject doubles, isolate dependencies, and observe behavior.
|
|
52
|
+
|
|
53
|
+
4. **Break Dependencies Deliberately**
|
|
54
|
+
- Remove direct dependence on time, randomness, files, network, process environment, globals, frameworks, and static construction where they block testing.
|
|
55
|
+
|
|
56
|
+
5. **Leave the Code More Changeable**
|
|
57
|
+
- Every change should ideally improve testability, visibility, or modularity.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Default Workflow for Legacy Changes
|
|
62
|
+
|
|
63
|
+
1. Identify the exact area affected.
|
|
64
|
+
2. Determine whether trusted tests already protect the behavior.
|
|
65
|
+
3. If not, add characterization tests around current behavior where possible.
|
|
66
|
+
4. Identify the dependency that makes change difficult.
|
|
67
|
+
5. Introduce or exploit a seam.
|
|
68
|
+
6. Break the blocking dependency.
|
|
69
|
+
7. Make the functional change.
|
|
70
|
+
8. Refactor for clarity and keep the seam or new structure if it still pays for itself.
|
|
71
|
+
|
|
72
|
+
Short form: identify change points, find test points, break dependencies, write tests, make changes, then refactor.
|
|
73
|
+
|
|
74
|
+
Do not start by cleaning the whole module.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Testing Strategy Rules
|
|
79
|
+
|
|
80
|
+
### Characterization Tests
|
|
81
|
+
1. Use characterization tests when you do not yet know whether the current behavior is intentional.
|
|
82
|
+
2. Test externally visible behavior first.
|
|
83
|
+
3. Prefer narrow tests around the slice you are about to modify.
|
|
84
|
+
4. Capture ugly behavior if real consumers rely on it.
|
|
85
|
+
5. Once behavior is protected, improve structure safely.
|
|
86
|
+
6. Mark suspicious current behavior for clarification instead of silently "fixing" it during characterization.
|
|
87
|
+
7. Use sensing variables or temporary probes only to confirm that a test reaches the intended path; remove them after use.
|
|
88
|
+
|
|
89
|
+
### New Behavior Tests
|
|
90
|
+
1. Add focused tests for the requested change.
|
|
91
|
+
2. Keep old behavior tests unless the behavior change is intentional.
|
|
92
|
+
3. Separate tests that describe legacy behavior from tests that describe the new requirement when useful.
|
|
93
|
+
|
|
94
|
+
### Testability Improvements
|
|
95
|
+
1. Make dependencies explicit.
|
|
96
|
+
2. Remove hard-coded collaborators.
|
|
97
|
+
3. Break apart mixed responsibilities that force expensive setup.
|
|
98
|
+
4. Reduce constructor side effects and static initialization side effects.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Seam Rules
|
|
103
|
+
|
|
104
|
+
### What Counts as a Useful Seam
|
|
105
|
+
A useful seam is any boundary that allows substitution, observation, or interception.
|
|
106
|
+
|
|
107
|
+
Examples:
|
|
108
|
+
- constructor injection
|
|
109
|
+
- parameter injection
|
|
110
|
+
- extracted method
|
|
111
|
+
- wrapper around static call
|
|
112
|
+
- adapter around framework object
|
|
113
|
+
- factory indirection
|
|
114
|
+
- module boundary
|
|
115
|
+
- link seam, import seam, or preprocessing seam where the language/build system supports it
|
|
116
|
+
- subclass seam when forced by language constraints
|
|
117
|
+
|
|
118
|
+
### Required Behavior
|
|
119
|
+
1. Use the smallest seam that unlocks the change.
|
|
120
|
+
2. Prefer explicit seams over magical test hooks.
|
|
121
|
+
3. Prefer seams that remain useful after the current task.
|
|
122
|
+
4. Create seams near hard dependencies, not randomly in the code.
|
|
123
|
+
5. Separate sensing from separation: decide whether the seam observes behavior, substitutes a dependency, or both.
|
|
124
|
+
6. Use link and preprocessing seams carefully; they can unlock tests but do not usually improve design by themselves.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Dependency Breaking Rules
|
|
129
|
+
|
|
130
|
+
When legacy code is hard to test, first look for these dependency types:
|
|
131
|
+
|
|
132
|
+
### Hidden Inputs
|
|
133
|
+
- current time
|
|
134
|
+
- random values
|
|
135
|
+
- environment variables
|
|
136
|
+
- thread-local state
|
|
137
|
+
- static singletons
|
|
138
|
+
- global configuration
|
|
139
|
+
- implicit current user or request
|
|
140
|
+
|
|
141
|
+
### Hard Outputs
|
|
142
|
+
- direct file writes
|
|
143
|
+
- direct network calls
|
|
144
|
+
- process exits
|
|
145
|
+
- direct database writes
|
|
146
|
+
- direct message publication
|
|
147
|
+
- logging used as control flow
|
|
148
|
+
|
|
149
|
+
### Construction Problems
|
|
150
|
+
- constructors that do real work
|
|
151
|
+
- new allocations of complex collaborators buried inside methods
|
|
152
|
+
- factory calls hidden deep in behavior
|
|
153
|
+
- object graphs built in the middle of logic
|
|
154
|
+
|
|
155
|
+
### Required Moves
|
|
156
|
+
- wrap static and global access
|
|
157
|
+
- inject clocks, random generators, external interfaces, and hard collaborators
|
|
158
|
+
- split construction from use
|
|
159
|
+
- extract side effects behind explicit collaborators
|
|
160
|
+
- narrow the code under test to a manageable slice
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Test Selection and Understanding Rules
|
|
165
|
+
|
|
166
|
+
1. Use effect sketches when the impact of a change is unclear.
|
|
167
|
+
2. Start from the change point and trace affected values, calls, fields, outputs, and collaborators outward.
|
|
168
|
+
3. Choose test points where effects can be observed with useful precision.
|
|
169
|
+
4. Use interception points when several planned changes can be protected by one broader test.
|
|
170
|
+
5. Use pinch points when many effects pass through one narrow point.
|
|
171
|
+
6. Treat broad tests at interception points as a first step toward narrower tests.
|
|
172
|
+
7. Use scratch refactoring to understand code, but discard it unless later backed by tests and review.
|
|
173
|
+
8. Sketch, mark, or group responsibilities in large code before moving behavior.
|
|
174
|
+
9. Do not check in exploratory restructuring that was only used to learn.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Preferred Legacy Techniques
|
|
179
|
+
|
|
180
|
+
### Sprout Method
|
|
181
|
+
Use when new behavior can be added without deeply editing fragile code.
|
|
182
|
+
|
|
183
|
+
Rules (MUST unless marked SHOULD or MUST NOT):
|
|
184
|
+
- extract the new behavior into a new method
|
|
185
|
+
- keep the old code mostly untouched
|
|
186
|
+
- route to the new method from a small insertion point
|
|
187
|
+
|
|
188
|
+
### Sprout Class
|
|
189
|
+
Use when a new responsibility does not fit the old class or the old class is too risky to reshape first.
|
|
190
|
+
|
|
191
|
+
Rules (MUST unless marked SHOULD or MUST NOT):
|
|
192
|
+
- add a focused new collaborator
|
|
193
|
+
- delegate from the legacy class
|
|
194
|
+
- slowly move behavior over if later justified
|
|
195
|
+
|
|
196
|
+
### Wrap Method
|
|
197
|
+
Use when you need pre/post behavior around a risky method or a better way to observe effects.
|
|
198
|
+
|
|
199
|
+
### Wrap Class
|
|
200
|
+
Use when a class is too hard to test directly and behavior can be mediated through a new abstraction.
|
|
201
|
+
|
|
202
|
+
### Extract and Override Call
|
|
203
|
+
Use only when language constraints leave few better options.
|
|
204
|
+
Prefer composition once a cleaner route appears.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Dependency-Breaking Technique Index
|
|
209
|
+
|
|
210
|
+
- USE Adapt Parameter when a method needs only a narrow view of a hard-to-create parameter.
|
|
211
|
+
- USE Break Out Method Object when a large method has local state that blocks extraction and testing.
|
|
212
|
+
- USE Definition Completion when missing definitions block tests in languages that allow completion in test code.
|
|
213
|
+
- USE Encapsulate Global References when globals or singletons prevent substitution.
|
|
214
|
+
- USE Expose Static Method when useful logic does not need instance state but is trapped behind instance setup.
|
|
215
|
+
- USE Extract and Override Factory Method when construction of a hard dependency must vary under test.
|
|
216
|
+
- USE Extract Implementer or Extract Interface when concrete dependencies make compilation or substitution hard.
|
|
217
|
+
- USE Introduce Instance Delegator when static behavior needs an instance seam.
|
|
218
|
+
- USE Parameterize Constructor or Parameterize Method when hidden collaborators should become explicit inputs.
|
|
219
|
+
- USE Primitivize Parameter only when the real type is too costly to bring into a harness and primitive data is enough for the new logic.
|
|
220
|
+
- USE Pull Up Feature or Push Down Dependency to move behavior or dependencies to a more testable level in a hierarchy.
|
|
221
|
+
- USE Replace Global Reference with Getter when direct global access needs a seam.
|
|
222
|
+
- USE Subclass and Override Method only when safer composition seams are not available.
|
|
223
|
+
- USE Supersede Instance Variable when a test needs to replace a hard dependency held in a field.
|
|
224
|
+
- USE Template Redefinition, Text Redefinition, link seams, or preprocessing seams only when language or build constraints make ordinary object seams impractical.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Legacy Refactoring Heuristics
|
|
229
|
+
|
|
230
|
+
1. Work near the change point, not across the whole system.
|
|
231
|
+
2. Prefer one small dependency break over a broad redesign.
|
|
232
|
+
3. If a test requires too much setup, the design is telling you something useful.
|
|
233
|
+
4. If code is impossible to observe, expose outcomes through smaller units.
|
|
234
|
+
5. If code is impossible to invoke without full runtime setup, isolate the policy from the runtime.
|
|
235
|
+
6. If code depends on many details, separate policy from mechanism.
|
|
236
|
+
7. If the old code cannot be safely changed, insert new code beside it and redirect gradually.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Handling Risky Areas
|
|
241
|
+
|
|
242
|
+
### Large Methods
|
|
243
|
+
- carve out pure computation first
|
|
244
|
+
- isolate side effects second
|
|
245
|
+
- add tests around extracted parts
|
|
246
|
+
- avoid editing many branches at once
|
|
247
|
+
|
|
248
|
+
### Static and Global Dependencies
|
|
249
|
+
- create a wrapper or façade
|
|
250
|
+
- move callers to the wrapper
|
|
251
|
+
- inject the wrapper where possible
|
|
252
|
+
- reduce direct calls incrementally
|
|
253
|
+
|
|
254
|
+
### Database-Heavy Code
|
|
255
|
+
- separate query and mapping concerns from policy
|
|
256
|
+
- test policy without a real database where possible
|
|
257
|
+
- keep integration tests for actual persistence behavior
|
|
258
|
+
|
|
259
|
+
### UI or Framework Code
|
|
260
|
+
- move decision logic out of handlers and callbacks
|
|
261
|
+
- test the moved logic independently
|
|
262
|
+
- keep adapters thin
|
|
263
|
+
|
|
264
|
+
### Constructors Doing Too Much
|
|
265
|
+
- stop doing I/O, network, or configuration lookup in constructors
|
|
266
|
+
- move setup into factories, builders, or composition roots
|
|
267
|
+
- keep constructed objects easy to instantiate under test
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Review Rules
|
|
272
|
+
|
|
273
|
+
When reviewing legacy-oriented changes, actively look for:
|
|
274
|
+
- no tests around modified logic
|
|
275
|
+
- structural and behavioral changes mixed together
|
|
276
|
+
- broad edits in poorly understood modules
|
|
277
|
+
- hidden global dependencies left untouched
|
|
278
|
+
- hard-coded collaborators
|
|
279
|
+
- direct static calls
|
|
280
|
+
- constructors with side effects
|
|
281
|
+
- business logic trapped in framework entry points
|
|
282
|
+
- places where a sprout method or sprout class lowers risk
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Forbidden Patterns
|
|
287
|
+
|
|
288
|
+
### Rewrite as the First Move
|
|
289
|
+
- replacing a subsystem before understanding current behavior
|
|
290
|
+
- rebuilding instead of gaining test leverage
|
|
291
|
+
- assuming old behavior is irrelevant because the code looks bad
|
|
292
|
+
|
|
293
|
+
### No-Safety Change
|
|
294
|
+
- changing legacy code with no tests or observation strategy
|
|
295
|
+
- large edits with no characterization
|
|
296
|
+
- relying on manual reasoning alone for risky behavior
|
|
297
|
+
|
|
298
|
+
### Hidden Dependency Expansion
|
|
299
|
+
- adding more globals, statics, ambient context, or framework reach-through in already hard-to-test code
|
|
300
|
+
- embedding new hard dependencies in the same style as the legacy code
|
|
301
|
+
|
|
302
|
+
### Cosmetic Refactoring Only
|
|
303
|
+
- renaming and formatting while leaving the real dependency knots intact
|
|
304
|
+
- “cleanup” that does not make the next change safer
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Code Generation Rules
|
|
309
|
+
|
|
310
|
+
When asked to modify legacy code, default to producing:
|
|
311
|
+
- characterization tests where needed
|
|
312
|
+
- small seams
|
|
313
|
+
- wrappers around hard dependencies
|
|
314
|
+
- explicit collaborators
|
|
315
|
+
- extracted pure logic
|
|
316
|
+
- minimal structural edits that unlock safe change
|
|
317
|
+
|
|
318
|
+
Preferred first moves:
|
|
319
|
+
- extract method
|
|
320
|
+
- wrap static call
|
|
321
|
+
- inject collaborator
|
|
322
|
+
- split construction from behavior
|
|
323
|
+
- move logic out of framework entry points
|
|
324
|
+
- introduce a focused new class for new behavior
|
|
325
|
+
- add a narrow characterization test
|
|
326
|
+
|
|
327
|
+
Preferred avoidance:
|
|
328
|
+
- huge dependency-breaking rewrites
|
|
329
|
+
- replacing old modules wholesale
|
|
330
|
+
- introducing large new architectures before basic seams exist
|
|
331
|
+
- mocking untestable structure instead of improving it
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Testing Rules
|
|
336
|
+
|
|
337
|
+
1. Prefer fast tests around the behavior you are changing.
|
|
338
|
+
2. Use characterization tests to lock current behavior before deeper edits.
|
|
339
|
+
3. Prefer tests at the highest level that still isolate the change safely.
|
|
340
|
+
4. Keep integration tests for real boundaries, but do not depend on them alone.
|
|
341
|
+
5. Once a seam exists, test through the seam.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Review Checklist
|
|
346
|
+
|
|
347
|
+
Before finalizing any change, verify:
|
|
348
|
+
- Did we treat untested code as risky legacy code?
|
|
349
|
+
- Did we capture current behavior where it was unclear?
|
|
350
|
+
- Did we create or exploit a seam?
|
|
351
|
+
- Did we reduce at least one hard dependency?
|
|
352
|
+
- Is the changed area easier to test than before?
|
|
353
|
+
- Did we avoid a rewrite as the first move?
|
|
354
|
+
- Did we keep edits local to the requested change?
|
|
355
|
+
- Did we separate structural changes from behavior changes where practical?
|
|
356
|
+
- Did we leave the code more changeable than we found it?
|
|
357
|
+
|
|
358
|
+
If any answer is no, revise before shipping.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Final Instruction
|
|
363
|
+
|
|
364
|
+
When uncertain, choose the smallest change that:
|
|
365
|
+
1. increases understanding
|
|
366
|
+
2. increases testability
|
|
367
|
+
3. breaks one hard dependency
|
|
368
|
+
4. preserves current behavior
|
|
369
|
+
5. makes the next change cheaper
|
|
370
|
+
|
|
371
|
+
Reject big rewrites and heroic cleanup when a seam and a test would do.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# OBEY Working Effectively with Legacy Code by Michael Feathers
|
|
2
|
+
|
|
3
|
+
## When to use
|
|
4
|
+
|
|
5
|
+
Use when changing code that is expensive to change safely because behavior is unclear, tests are weak or missing, dependencies are hidden, or runtime/framework setup blocks local feedback.
|
|
6
|
+
|
|
7
|
+
## Primary bias to correct
|
|
8
|
+
|
|
9
|
+
Gain control before improving design. Understand current behavior, protect what must stay, create the smallest useful seam, break the dependency that blocks feedback, make the requested change, then leave the area more testable.
|
|
10
|
+
|
|
11
|
+
## Decision rules
|
|
12
|
+
|
|
13
|
+
- Treat any area without trustworthy tests as legacy code; do not start with rewrite or module-wide cleanup unless that is explicitly required or clearly safer.
|
|
14
|
+
- Before editing, state the requested behavior change and the current behavior that must remain; characterize uncertain or suspicious behavior instead of silently fixing it.
|
|
15
|
+
- Follow the legacy loop: identify the change point, check existing protection, add characterization where possible, find or create a seam, break the blocking dependency, change behavior, then refactor locally.
|
|
16
|
+
- Prefer fast, focused tests around the slice being changed; use broader interception or integration tests only when they are the safest first observation point.
|
|
17
|
+
- Choose test points by tracing effects outward from the change point through values, calls, fields, outputs, collaborators, interception points, and pinch points.
|
|
18
|
+
- Use the smallest seam that allows substitution, observation, or interception; make clear whether the seam is for sensing, separation, or both.
|
|
19
|
+
- Break dependencies deliberately: expose hidden inputs, hard outputs, hard construction, globals, statics, ambient context, and framework callbacks only where they block testing or safe change.
|
|
20
|
+
- Keep behavior changes, structural refactorings, and cleanup separate; verify small steps and avoid checking in exploratory restructuring used only for understanding.
|
|
21
|
+
- When direct edits are risky, add behavior with sprout method, sprout class, wrap method, wrap class, or extract-and-override style moves, then fold the temporary structure into better design when tests support it.
|
|
22
|
+
- For hard-to-test methods, split construction from use, extract side effects behind collaborators, carve pure computation first, and isolate policy from runtime, persistence, UI, or framework mechanisms.
|
|
23
|
+
- Use dependency-breaking techniques according to the actual barrier: adapt narrow parameters, extract interfaces or implementers, parameterize constructors or methods, encapsulate globals, introduce instance delegators, override factories/calls, or use link/preprocessing seams only when ordinary object seams are impractical.
|
|
24
|
+
- In large code, sketch effects and group responsibilities before moving behavior; let excessive setup, impossible observation, and repeated changes point to smaller extracted responsibilities.
|
|
25
|
+
- During review, treat no tests around modified logic, mixed structural and behavioral edits, broad edits in poorly understood modules, hard-coded collaborators, global/static reach-through, constructor side effects, and business logic trapped in framework entry points as legacy-change risks.
|
|
26
|
+
- Reject changes that expand hidden dependencies, mock around untestable structure without improving it, rename or format while leaving the real dependency knots intact, or introduce large architecture before basic seams exist.
|
|
27
|
+
- Leave the touched area easier to understand, test, or change; do not mistake test-only seams, wrappers, subclass tricks, or build tricks for design improvement by themselves.
|
|
28
|
+
|
|
29
|
+
## Trigger rules
|
|
30
|
+
|
|
31
|
+
- When behavior is uncertain, consumers may rely on ugly behavior, or a branch/path is hard to prove, add characterization or another explicit observation path before changing semantics.
|
|
32
|
+
- When tests require too much setup or a class cannot be instantiated cheaply, break the first real barrier: constructor work, hidden allocation, factory call, global state, static construction, framework object, or hard parameter.
|
|
33
|
+
- When time, randomness, environment, thread-local state, current user/request, files, network, process exits, database writes, messages, or control-flow logging block repeatable tests, wrap or inject that boundary.
|
|
34
|
+
- When a large method or class defeats local reasoning, sketch effects, find interception or pinch points, extract pure computation first, and avoid editing many branches at once.
|
|
35
|
+
- When changing database-heavy, UI, framework, or API-boundary code, separate policy from query/mapping/persistence, handlers/callbacks, adapters, and runtime setup; keep real-boundary integration tests where they matter.
|
|
36
|
+
- When a seam is magical, temporary, public-for-test, subclass-only, link/preprocessor-based, or probe/sensing-variable-based, add a cleanup obligation and remove it once safer structure exists.
|
|
37
|
+
- When repeated edits cluster across several places, remove duplication incrementally under tests instead of launching a broad redesign.
|
|
38
|
+
- When rewrite or heroic cleanup feels tempting, choose the smallest sprout, wrap, seam, characterization, or refactoring step that makes today's requested change safer.
|
|
39
|
+
|
|
40
|
+
## Final checklist
|
|
41
|
+
|
|
42
|
+
- Untested or weakly tested area treated as legacy risk?
|
|
43
|
+
- Behavior delta and behavior-to-preserve stated?
|
|
44
|
+
- Uncertain current behavior characterized or explicitly observed?
|
|
45
|
+
- Tests close enough and fast enough to diagnose the change?
|
|
46
|
+
- Smallest useful seam chosen, with sensing vs separation clear?
|
|
47
|
+
- Blocking dependency reduced without expanding hidden dependencies?
|
|
48
|
+
- Behavior change, refactoring, and cleanup kept separate?
|
|
49
|
+
- Temporary seam or dependency-breaking trick has a cleanup path?
|
|
50
|
+
- Touched area is more understandable, testable, or changeable?
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# OBEY Working Effectively with Legacy Code by Michael Feathers
|
|
2
|
+
|
|
3
|
+
## When to use
|
|
4
|
+
|
|
5
|
+
Use when changing poorly tested or poorly understood code under a tight context budget.
|
|
6
|
+
|
|
7
|
+
## Primary bias to correct
|
|
8
|
+
|
|
9
|
+
Legacy work starts with control, not cleanup, rewrite, or elegance.
|
|
10
|
+
|
|
11
|
+
## Decision rules
|
|
12
|
+
|
|
13
|
+
- Treat code without trustworthy tests as legacy code: state what changes and what must remain.
|
|
14
|
+
- Characterize uncertain current behavior before changing it, including ugly behavior consumers may rely on.
|
|
15
|
+
- Use the legacy loop: find the change point, find an observation point, create or exploit a seam, break the blocking dependency, test, change, then refactor locally.
|
|
16
|
+
- Prefer fast focused tests; use broader harnesses only as temporary first coverage when no narrow test point exists.
|
|
17
|
+
- Create the narrowest useful seam for sensing or separation, and break only dependencies that block feedback.
|
|
18
|
+
- Use sprout, wrap, parameterize, inject, extract, or override moves when direct edits would be unsafe.
|
|
19
|
+
- Keep behavior changes, structural refactorings, and cleanup separate and small.
|
|
20
|
+
- Do not leave test-only seams, hidden dependencies, wrappers, globals, subclass tricks, or link/preprocessor tricks without a cleanup plan.
|
|
21
|
+
|
|
22
|
+
## Trigger rules
|
|
23
|
+
|
|
24
|
+
- When behavior is unclear, characterize first.
|
|
25
|
+
- When constructors, globals, statics, frameworks, I/O, clocks, randomness, environment, or deep object graphs block testing, break one dependency at the narrowest point.
|
|
26
|
+
- When a large method or class defeats local reasoning, sketch effects and create a seam before semantic edits.
|
|
27
|
+
- When rewrite or broad cleanup feels tempting, choose the next smaller verified move.
|
|
28
|
+
|
|
29
|
+
## Final checklist
|
|
30
|
+
|
|
31
|
+
- Behavior characterized?
|
|
32
|
+
- Feedback fast enough?
|
|
33
|
+
- Dependency isolated?
|
|
34
|
+
- One kind of change?
|
|
35
|
+
- Safer and clearer now?
|