@schilling.mark.a/software-methodology 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 (77) hide show
  1. package/.github/copilot-instructions.md +106 -0
  2. package/LICENSE +21 -0
  3. package/README.md +174 -0
  4. package/atdd-workflow/SKILL.md +117 -0
  5. package/atdd-workflow/references/green-phase.md +38 -0
  6. package/atdd-workflow/references/red-phase.md +62 -0
  7. package/atdd-workflow/references/refactor-phase.md +75 -0
  8. package/bdd-specification/SKILL.md +88 -0
  9. package/bdd-specification/references/example-mapping.md +105 -0
  10. package/bdd-specification/references/gherkin-patterns.md +214 -0
  11. package/cicd-pipeline/SKILL.md +64 -0
  12. package/cicd-pipeline/references/deployment-rollback.md +176 -0
  13. package/cicd-pipeline/references/environment-promotion.md +159 -0
  14. package/cicd-pipeline/references/pipeline-stages.md +198 -0
  15. package/clean-code/SKILL.md +77 -0
  16. package/clean-code/references/behavioral-patterns.md +329 -0
  17. package/clean-code/references/creational-patterns.md +197 -0
  18. package/clean-code/references/enterprise-patterns.md +334 -0
  19. package/clean-code/references/solid.md +230 -0
  20. package/clean-code/references/structural-patterns.md +238 -0
  21. package/continuous-improvement/SKILL.md +69 -0
  22. package/continuous-improvement/references/measurement.md +133 -0
  23. package/continuous-improvement/references/process-update.md +118 -0
  24. package/continuous-improvement/references/root-cause-analysis.md +144 -0
  25. package/dist/atdd-workflow.skill +0 -0
  26. package/dist/bdd-specification.skill +0 -0
  27. package/dist/cicd-pipeline.skill +0 -0
  28. package/dist/clean-code.skill +0 -0
  29. package/dist/continuous-improvement.skill +0 -0
  30. package/dist/green-implementation.skill +0 -0
  31. package/dist/product-strategy.skill +0 -0
  32. package/dist/story-mapping.skill +0 -0
  33. package/dist/ui-design-system.skill +0 -0
  34. package/dist/ui-design-workflow.skill +0 -0
  35. package/dist/ux-design.skill +0 -0
  36. package/dist/ux-research.skill +0 -0
  37. package/docs/INTEGRATION.md +229 -0
  38. package/docs/QUICKSTART.md +126 -0
  39. package/docs/SHARING.md +828 -0
  40. package/docs/SKILLS.md +296 -0
  41. package/green-implementation/SKILL.md +155 -0
  42. package/green-implementation/references/angular-patterns.md +239 -0
  43. package/green-implementation/references/common-rejections.md +180 -0
  44. package/green-implementation/references/playwright-patterns.md +321 -0
  45. package/green-implementation/references/rxjs-patterns.md +161 -0
  46. package/package.json +57 -0
  47. package/product-strategy/SKILL.md +71 -0
  48. package/product-strategy/references/business-model-canvas.md +199 -0
  49. package/product-strategy/references/canvas-alignment.md +108 -0
  50. package/product-strategy/references/value-proposition-canvas.md +159 -0
  51. package/project-templates/context.md.template +56 -0
  52. package/project-templates/test-strategy.md.template +87 -0
  53. package/story-mapping/SKILL.md +104 -0
  54. package/story-mapping/references/backbone.md +66 -0
  55. package/story-mapping/references/release-planning.md +92 -0
  56. package/story-mapping/references/task-template.md +78 -0
  57. package/story-mapping/references/walking-skeleton.md +63 -0
  58. package/ui-design-system/SKILL.md +48 -0
  59. package/ui-design-system/references/accessibility.md +134 -0
  60. package/ui-design-system/references/components.md +257 -0
  61. package/ui-design-system/references/design-tokens.md +209 -0
  62. package/ui-design-system/references/layout.md +136 -0
  63. package/ui-design-system/references/typography.md +114 -0
  64. package/ui-design-workflow/SKILL.md +90 -0
  65. package/ui-design-workflow/references/acceptance-targets.md +144 -0
  66. package/ui-design-workflow/references/component-selection.md +108 -0
  67. package/ui-design-workflow/references/scenario-to-ui.md +151 -0
  68. package/ui-design-workflow/references/screen-flows.md +116 -0
  69. package/ux-design/SKILL.md +75 -0
  70. package/ux-design/references/information-architecture.md +144 -0
  71. package/ux-design/references/interaction-patterns.md +141 -0
  72. package/ux-design/references/onboarding.md +159 -0
  73. package/ux-design/references/usability-evaluation.md +132 -0
  74. package/ux-research/SKILL.md +75 -0
  75. package/ux-research/references/journey-mapping.md +168 -0
  76. package/ux-research/references/mental-models.md +106 -0
  77. package/ux-research/references/personas.md +102 -0
@@ -0,0 +1,329 @@
1
+ # Behavioral Patterns
2
+
3
+ Patterns for managing communication and responsibility between objects.
4
+
5
+ ---
6
+
7
+ ## Strategy
8
+
9
+ **When to use:**
10
+ - A family of algorithms exists, and any one can be selected at runtime
11
+ - A class has a behavior that varies based on a condition, implemented as an if/else or switch
12
+ - You want to isolate each algorithm's implementation in its own class
13
+
14
+ **Smell that triggers it:**
15
+ ```
16
+ class InvoicePrinter:
17
+ print(invoice):
18
+ if format == "pdf":
19
+ // 30 lines of PDF logic
20
+ elif format == "html":
21
+ // 25 lines of HTML logic
22
+ elif format == "csv":
23
+ // 20 lines of CSV logic ← new format = edit this class
24
+ ```
25
+
26
+ **Solution:**
27
+ ```
28
+ interface PrintStrategy:
29
+ print(invoice): output
30
+
31
+ class PDFPrintStrategy implements PrintStrategy:
32
+ print(invoice): // PDF logic
33
+
34
+ class HTMLPrintStrategy implements PrintStrategy:
35
+ print(invoice): // HTML logic
36
+
37
+ class InvoicePrinter:
38
+ constructor(strategy: PrintStrategy)
39
+ print(invoice): return this.strategy.print(invoice)
40
+
41
+ // Swap strategy at runtime
42
+ printer = new InvoicePrinter(new PDFPrintStrategy())
43
+ printer.print(invoice)
44
+
45
+ printer = new InvoicePrinter(new HTMLPrintStrategy())
46
+ printer.print(invoice)
47
+ ```
48
+
49
+ **Key insight:** Strategy is the most commonly needed behavioral pattern. Whenever you see a method with branching logic that selects between algorithms, Strategy is almost certainly the answer. New algorithm = new class, zero edits to existing code.
50
+
51
+ ---
52
+
53
+ ## Command
54
+
55
+ **When to use:**
56
+ - You need to encapsulate an action as an object
57
+ - You need undo/redo capability
58
+ - You need to queue, log, or sequence operations
59
+ - You want to decouple the object that performs the action from the object that triggers it
60
+
61
+ **Smell that triggers it:**
62
+ - A UI button or API endpoint directly calls business logic
63
+ - You need a history of operations (for undo, audit trail, or replay)
64
+ - You need to schedule actions for later execution
65
+
66
+ **Solution:**
67
+ ```
68
+ interface Command:
69
+ execute()
70
+ undo()
71
+
72
+ class AddLineItemCommand implements Command:
73
+ constructor(invoice, lineItem)
74
+ execute():
75
+ this.invoice.addLineItem(this.lineItem)
76
+ undo():
77
+ this.invoice.removeLineItem(this.lineItem)
78
+
79
+ class ApplyDiscountCommand implements Command:
80
+ constructor(invoice, discount)
81
+ private previousDiscount: Discount
82
+
83
+ execute():
84
+ this.previousDiscount = this.invoice.getDiscount()
85
+ this.invoice.setDiscount(this.discount)
86
+ undo():
87
+ this.invoice.setDiscount(this.previousDiscount)
88
+
89
+ // Command history enables undo
90
+ class InvoiceEditor:
91
+ private history: Command[] = []
92
+
93
+ execute(command: Command):
94
+ command.execute()
95
+ this.history.push(command)
96
+
97
+ undo():
98
+ lastCommand = this.history.pop()
99
+ lastCommand.undo()
100
+
101
+ // Usage
102
+ editor.execute(new AddLineItemCommand(invoice, consulting))
103
+ editor.execute(new ApplyDiscountCommand(invoice, tenPercent))
104
+ editor.undo() ← removes discount
105
+ editor.undo() ← removes line item
106
+ ```
107
+
108
+ **Key insight:** Each command is a self-contained unit of work. The history stack makes undo trivial. The same pattern works for audit logging — just persist the commands instead of discarding them.
109
+
110
+ ---
111
+
112
+ ## Observer
113
+
114
+ **When to use:**
115
+ - An object's state change should notify one or more other objects
116
+ - The number of interested parties is unknown or varies at runtime
117
+ - You want loose coupling between the thing that changed and the things that react
118
+
119
+ **Smell that triggers it:**
120
+ - A method that directly calls multiple other systems after doing its work
121
+ - Adding a new reaction to an event requires editing the source class
122
+
123
+ **Solution:**
124
+ ```
125
+ interface Observer:
126
+ update(event: Event)
127
+
128
+ interface Observable:
129
+ subscribe(observer: Observer)
130
+ unsubscribe(observer: Observer)
131
+ notify(event: Event)
132
+
133
+ class Invoice implements Observable:
134
+ private observers: Observer[] = []
135
+
136
+ subscribe(observer): this.observers.push(observer)
137
+ unsubscribe(observer): this.observers = this.observers.filter(o => o !== observer)
138
+
139
+ notify(event):
140
+ this.observers.forEach(o => o.update(event))
141
+
142
+ markPaid(transactionId):
143
+ this.status = "paid"
144
+ this.notify(new InvoicePaidEvent(this, transactionId))
145
+
146
+ // Observers — each handles one concern
147
+ class EmailNotifier implements Observer:
148
+ update(event):
149
+ if event is InvoicePaidEvent:
150
+ sendConfirmationEmail(event.invoice.customer)
151
+
152
+ class AccountingSync implements Observer:
153
+ update(event):
154
+ if event is InvoicePaidEvent:
155
+ syncToAccountingSystem(event.invoice)
156
+
157
+ class AuditLogger implements Observer:
158
+ update(event):
159
+ logEvent(event)
160
+
161
+ // Wire up at startup
162
+ invoice.subscribe(new EmailNotifier())
163
+ invoice.subscribe(new AccountingSync())
164
+ invoice.subscribe(new AuditLogger())
165
+ ```
166
+
167
+ **Key insight:** `Invoice` knows nothing about email, accounting, or audit logging. New reaction = new observer class + wire it up. Zero changes to `Invoice`.
168
+
169
+ ---
170
+
171
+ ## State
172
+
173
+ **When to use:**
174
+ - An object's behavior changes based on its internal state
175
+ - A large if/else or switch block checks state and branches behavior
176
+ - State transitions have rules that should be enforced
177
+
178
+ **Smell that triggers it:**
179
+ ```
180
+ class Invoice:
181
+ pay():
182
+ if this.status == "draft":
183
+ throw Error("Cannot pay a draft")
184
+ if this.status == "paid":
185
+ throw Error("Already paid")
186
+ if this.status == "cancelled":
187
+ throw Error("Cannot pay cancelled invoice")
188
+ this.status = "paid" ← state logic scattered through every method
189
+ ```
190
+
191
+ **Solution:**
192
+ ```
193
+ interface InvoiceState:
194
+ pay(invoice)
195
+ cancel(invoice)
196
+ send(invoice)
197
+
198
+ class DraftState implements InvoiceState:
199
+ pay(invoice): throw Error("Finalize the invoice before payment")
200
+ cancel(invoice): invoice.setState(new CancelledState())
201
+ send(invoice): invoice.setState(new SentState())
202
+
203
+ class SentState implements InvoiceState:
204
+ pay(invoice): invoice.setState(new PaidState())
205
+ cancel(invoice): invoice.setState(new CancelledState())
206
+ send(invoice): throw Error("Already sent")
207
+
208
+ class PaidState implements InvoiceState:
209
+ pay(invoice): throw Error("Already paid")
210
+ cancel(invoice): throw Error("Cannot cancel a paid invoice")
211
+ send(invoice): throw Error("Already paid")
212
+
213
+ class CancelledState implements InvoiceState:
214
+ pay(invoice): throw Error("Cannot pay a cancelled invoice")
215
+ cancel(invoice): throw Error("Already cancelled")
216
+ send(invoice): throw Error("Cannot send a cancelled invoice")
217
+
218
+ class Invoice:
219
+ private state: InvoiceState = new DraftState()
220
+ setState(state): this.state = state
221
+ pay(): this.state.pay(this) ← delegates entirely to state object
222
+ cancel(): this.state.cancel(this)
223
+ send(): this.state.send(this)
224
+ ```
225
+
226
+ **Key insight:** Each state is a class. Valid transitions are explicit. Invalid transitions throw immediately. Adding a new state means one new class. The `Invoice` class itself never changes.
227
+
228
+ ---
229
+
230
+ ## Chain of Responsibility
231
+
232
+ **When to use:**
233
+ - A request must pass through a sequence of handlers until one handles it
234
+ - The set of handlers or their order may vary at runtime
235
+ - Each handler decides whether to handle the request or pass it along
236
+
237
+ **Smell that triggers it:**
238
+ - A series of if/else checks where each check either handles the case or falls through to the next
239
+ - Validation logic that applies multiple rules in sequence
240
+
241
+ **Solution:**
242
+ ```
243
+ abstract class ValidationHandler:
244
+ private next: ValidationHandler
245
+
246
+ setNext(handler: ValidationHandler): ValidationHandler
247
+ this.next = handler
248
+ return handler
249
+
250
+ validate(invoice): ValidationResult
251
+ if this.next:
252
+ return this.next.validate(invoice)
253
+ return ValidationResult.pass()
254
+
255
+ class CustomerExistsHandler extends ValidationHandler:
256
+ validate(invoice):
257
+ if not customerExists(invoice.customerId):
258
+ return ValidationResult.fail("Customer not found")
259
+ return super.validate(invoice) ← pass to next handler
260
+
261
+ class LineItemsRequiredHandler extends ValidationHandler:
262
+ validate(invoice):
263
+ if invoice.lineItems.length == 0:
264
+ return ValidationResult.fail("At least one line item required")
265
+ return super.validate(invoice)
266
+
267
+ class MaxAmountHandler extends ValidationHandler:
268
+ validate(invoice):
269
+ if invoice.total() > maxAllowed:
270
+ return ValidationResult.fail("Exceeds maximum amount")
271
+ return super.validate(invoice)
272
+
273
+ // Wire the chain
274
+ customerCheck = new CustomerExistsHandler()
275
+ lineItemCheck = new LineItemsRequiredHandler()
276
+ amountCheck = new MaxAmountHandler()
277
+
278
+ customerCheck.setNext(lineItemCheck).setNext(amountCheck)
279
+
280
+ // Validate — passes through chain until failure or all pass
281
+ result = customerCheck.validate(invoice)
282
+ ```
283
+
284
+ **Key insight:** Each handler has one responsibility. New validation rule = one new handler class, inserted into the chain. Order is controlled at wiring time.
285
+
286
+ ---
287
+
288
+ ## Template Method
289
+
290
+ **When to use:**
291
+ - Multiple classes share the same algorithm skeleton but differ in specific steps
292
+ - You want to enforce an algorithm's structure while letting subclasses customize individual steps
293
+
294
+ **Smell that triggers it:**
295
+ - Copy-pasted methods across classes that differ only in one or two steps
296
+ - An algorithm where the sequence of steps is fixed but the implementation of each step varies
297
+
298
+ **Solution:**
299
+ ```
300
+ abstract class InvoiceExporter:
301
+ // Template method — defines the algorithm skeleton. Final = cannot be overridden.
302
+ export(invoice): final
303
+ data = this.extractData(invoice) ← step 1: subclass implements
304
+ validated = this.validate(data) ← step 2: subclass implements
305
+ formatted = this.format(validated) ← step 3: subclass implements
306
+ this.output(formatted) ← step 4: subclass implements
307
+ return formatted
308
+
309
+ abstract extractData(invoice): ExportData
310
+ abstract validate(data): ExportData
311
+ abstract format(data): string
312
+ abstract output(formatted): void
313
+
314
+ class PDFExporter extends InvoiceExporter:
315
+ extractData(invoice): // PDF-specific extraction
316
+ validate(data): // PDF-specific validation
317
+ format(data): // PDF rendering
318
+ output(formatted): // Write to PDF file
319
+
320
+ class CSVExporter extends InvoiceExporter:
321
+ extractData(invoice): // CSV-specific extraction
322
+ validate(data): // CSV-specific validation
323
+ format(data): // CSV formatting
324
+ output(formatted): // Write to CSV file
325
+ ```
326
+
327
+ **Key insight:** The algorithm sequence (extract → validate → format → output) is defined once in the base class. Each exporter only implements the steps. If the sequence needs to change, it changes in one place.
328
+
329
+ **Note:** Template Method uses inheritance. If you find yourself wanting the same flexibility without inheritance, Strategy achieves the same result with composition — generally preferred in modern code.
@@ -0,0 +1,197 @@
1
+ # Creational Patterns
2
+
3
+ Patterns for controlling how objects are created.
4
+
5
+ ---
6
+
7
+ ## Factory Method
8
+
9
+ **When to use:**
10
+ - Object creation logic is complex or varies based on a condition
11
+ - You need to decouple the caller from the concrete class being created
12
+ - A new type of object can be added without changing the creation site
13
+
14
+ **Smell that triggers it:**
15
+ ```
16
+ if type == "pdf":
17
+ doc = new PDFExporter()
18
+ elif type == "csv":
19
+ doc = new CSVExporter()
20
+ elif type == "xlsx": ← new type = edit this block
21
+ doc = new XLSXExporter()
22
+ ```
23
+
24
+ **Solution:**
25
+ ```
26
+ interface Exporter:
27
+ export(data)
28
+
29
+ class ExporterFactory:
30
+ static create(type: string): Exporter
31
+ registry = { "pdf": PDFExporter, "csv": CSVExporter, "xlsx": XLSXExporter }
32
+ return new registry[type]()
33
+
34
+ // Usage — caller knows nothing about concrete types
35
+ exporter = ExporterFactory.create(requestedFormat)
36
+ exporter.export(invoiceData)
37
+ ```
38
+
39
+ **Key insight:** Adding a new format means adding a new class and registering it. The calling code never changes.
40
+
41
+ ---
42
+
43
+ ## Abstract Factory
44
+
45
+ **When to use:**
46
+ - You need to create families of related objects that must be used together
47
+ - The system should be independent of how its products are created
48
+ - A "theme" or "variant" determines which set of objects is created
49
+
50
+ **Smell that triggers it:**
51
+ - Multiple factory methods that always get called together
52
+ - Objects that must be consistent with each other (e.g., a UI theme where button, input, and modal must all match)
53
+
54
+ **Solution:**
55
+ ```
56
+ interface UIFactory:
57
+ createButton(): Button
58
+ createInput(): Input
59
+ createModal(): Modal
60
+
61
+ class DarkThemeFactory implements UIFactory:
62
+ createButton(): return new DarkButton()
63
+ createInput(): return new DarkInput()
64
+ createModal(): return new DarkModal()
65
+
66
+ class LightThemeFactory implements UIFactory:
67
+ createButton(): return new LightButton()
68
+ createInput(): return new LightInput()
69
+ createModal(): return new LightModal()
70
+
71
+ // App receives a factory — knows nothing about theme internals
72
+ class App:
73
+ constructor(factory: UIFactory)
74
+ this.button = factory.createButton()
75
+ this.input = factory.createInput()
76
+ ```
77
+
78
+ **Key insight:** New theme = new factory class. The rest of the application is completely unaware.
79
+
80
+ ---
81
+
82
+ ## Builder
83
+
84
+ **When to use:**
85
+ - An object requires many parameters, most of which are optional
86
+ - Construction requires a specific sequence of steps
87
+ - The same construction process can produce different representations
88
+
89
+ **Smell that triggers it:**
90
+ ```
91
+ // Telescoping constructor — grows with every optional field
92
+ invoice = new Invoice(customer, date, null, null, "standard", null, true)
93
+ // What does the 5th null mean? Nobody knows.
94
+ ```
95
+
96
+ **Solution:**
97
+ ```
98
+ class InvoiceBuilder:
99
+ private invoice = new Invoice()
100
+
101
+ setCustomer(customer): this.invoice.customer = customer; return this
102
+ setDueDate(date): this.invoice.dueDate = date; return this
103
+ setTaxRule(rule): this.invoice.taxRule = rule; return this
104
+ setDiscount(discount): this.invoice.discount = discount; return this
105
+ markAsPriority(): this.invoice.priority = true; return this
106
+
107
+ build(): Invoice
108
+ validate(this.invoice) ← enforce required fields here
109
+ return this.invoice
110
+
111
+ // Usage — reads like a specification
112
+ invoice = new InvoiceBuilder()
113
+ .setCustomer(acmeCorp)
114
+ .setDueDate(nextMonth)
115
+ .setTaxRule(standardRate)
116
+ .markAsPriority()
117
+ .build()
118
+ ```
119
+
120
+ **Key insight:** Each step is named. Optional fields are simply omitted. Validation happens at `build()`, not scattered across constructors.
121
+
122
+ **Test usage:** Builders are especially valuable in test fixtures. A `TestInvoiceBuilder` can set sensible defaults, and each test overrides only what it needs:
123
+ ```
124
+ // Test only cares about discount behavior — everything else is default
125
+ invoice = TestInvoiceBuilder.defaults().setDiscount(0.15).build()
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Singleton
131
+
132
+ **When to use:**
133
+ - Exactly one instance must exist and be globally accessible
134
+ - The instance controls access to a shared resource (config, logger, registry)
135
+
136
+ **Smell that triggers it:**
137
+ - Multiple places trying to create the same resource independently
138
+ - Need for a single shared state across the application
139
+
140
+ **Solution:**
141
+ ```
142
+ class Configuration:
143
+ private static instance: Configuration
144
+ private settings: Map
145
+
146
+ private constructor(): ← private — nobody else can create one
147
+ this.settings = loadFromFile()
148
+
149
+ static getInstance(): Configuration
150
+ if not this.instance:
151
+ this.instance = new Configuration()
152
+ return this.instance
153
+
154
+ get(key: string): string
155
+ return this.settings[key]
156
+ ```
157
+
158
+ **Warning:** Singleton is the most abused pattern. Do NOT use it for:
159
+ - Database connections (use a connection pool instead)
160
+ - Services that could benefit from multiple instances
161
+ - Anything that makes unit testing harder
162
+
163
+ If you find yourself reaching for Singleton to solve a dependency problem, DIP (from `references/solid.md`) is almost certainly the right answer instead.
164
+
165
+ ---
166
+
167
+ ## Prototype
168
+
169
+ **When to use:**
170
+ - Creating new objects is expensive and you have an existing instance to clone
171
+ - The class hierarchy mirrors the variety of objects you want to create
172
+
173
+ **Smell that triggers it:**
174
+ - Object creation involves heavy initialization (network call, file read, complex calculation)
175
+ - You need many similar objects that differ only slightly
176
+
177
+ **Solution:**
178
+ ```
179
+ interface Cloneable:
180
+ clone(): self
181
+
182
+ class InvoiceTemplate implements Cloneable:
183
+ clone():
184
+ copy = new InvoiceTemplate()
185
+ copy.taxRule = this.taxRule
186
+ copy.paymentTerms = this.paymentTerms
187
+ copy.header = this.header
188
+ return copy
189
+
190
+ // Usage — clone and customize, skip expensive setup
191
+ template = standardInvoiceTemplate
192
+ newInvoice = template.clone()
193
+ newInvoice.setCustomer(acmeCorp)
194
+ newInvoice.addLineItem(consultingService)
195
+ ```
196
+
197
+ **Key insight:** Useful when you have template objects and need many variations quickly. Less common than Factory or Builder — apply only when creation cost is genuinely high.