@martinffx/atelier 1.0.2 → 1.2.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 (42) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +6 -5
  3. package/package.json +1 -1
  4. package/skills/{code-conventional-commit → code-commit}/SKILL.md +16 -1
  5. package/skills/code-commit/references/pr-workflow.md +145 -0
  6. package/skills/code-debug/SKILL.md +119 -191
  7. package/skills/code-debug/references/bug-patterns.md +71 -0
  8. package/skills/code-debug/references/techniques.md +44 -4
  9. package/skills/code-handoff/SKILL.md +16 -0
  10. package/skills/code-review/references/reviewers.md +3 -11
  11. package/skills/code-review/references/rq.md +4 -5
  12. package/skills/oracle-architect/SKILL.md +82 -1
  13. package/skills/oracle-architect/references/ddd-patterns.md +101 -0
  14. package/skills/oracle-architect/references/interface-design.md +203 -0
  15. package/skills/oracle-doubt/SKILL.md +252 -0
  16. package/skills/oracle-doubt/references/adversarial-prompt.md +47 -0
  17. package/skills/oracle-grillme/SKILL.md +160 -0
  18. package/skills/oracle-grillme/references/ADR-FORMAT.md +47 -0
  19. package/skills/oracle-grillme/references/CONTEXT-FORMAT.md +63 -0
  20. package/skills/oracle-security/SKILL.md +225 -0
  21. package/skills/oracle-security/references/security-patterns.md +236 -0
  22. package/skills/oracle-security/references/threat-modeling-guide.md +137 -0
  23. package/skills/spec-finish/SKILL.md +2 -5
  24. package/skills/spec-implement/SKILL.md +1 -1
  25. package/skills/code-conventional-commit/evals/evals.json +0 -23
  26. package/skills/code-conventional-commit/workspace/iteration-1/eval-0/with_skill/outputs/commit-message.txt +0 -4
  27. package/skills/code-conventional-commit/workspace/iteration-1/eval-0/with_skill/outputs/eval_metadata.json +0 -6
  28. package/skills/code-conventional-commit/workspace/iteration-1/eval-1/with_skill/outputs/eval_metadata.json +0 -6
  29. package/skills/code-conventional-commit/workspace/iteration-1/eval-1/with_skill/outputs/validation-result.txt +0 -16
  30. package/skills/code-conventional-commit/workspace/iteration-1/eval-2/with_skill/outputs/commit-message.txt +0 -8
  31. package/skills/code-conventional-commit/workspace/iteration-1/eval-2/with_skill/outputs/eval_metadata.json +0 -6
  32. package/skills/code-conventional-commit/workspace/iteration-1/report.html +0 -1325
  33. package/skills/code-git-worktrees/SKILL.md +0 -218
  34. package/skills/code-perf/SKILL.md +0 -163
  35. package/skills/code-perf/references/common-bottlenecks.md +0 -124
  36. package/skills/code-perf/references/optimization-patterns.md +0 -167
  37. package/skills/code-perf/references/profiling-tools.md +0 -126
  38. package/skills/code-security/SKILL.md +0 -183
  39. package/skills/code-security/references/owasp-top-10.md +0 -129
  40. package/skills/code-security/references/security-tools.md +0 -102
  41. package/skills/code-security/references/vulnerability-patterns.md +0 -129
  42. package/skills/code-stacked-commit/SKILL.md +0 -155
@@ -0,0 +1,71 @@
1
+ # Common Bug Patterns
2
+
3
+ Quick reference for recognising and fixing recurring bug categories.
4
+
5
+ ---
6
+
7
+ ## Null/Undefined Errors
8
+
9
+ **Symptoms:** Cannot read property X of undefined
10
+
11
+ **Debug approach:**
12
+ 1. Find where the value becomes undefined
13
+ 2. Trace back where it should come from
14
+ 3. Add guard or fix source
15
+
16
+ **Fix patterns:**
17
+ ```javascript
18
+ // Before
19
+ const name = user.profile.name;
20
+
21
+ // After
22
+ const name = user?.profile?.name ?? 'Unknown';
23
+ // or
24
+ if (!user?.profile) return;
25
+ const name = user.profile.name;
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Race Conditions
31
+
32
+ **Symptoms:** Intermittent failures, flaky tests
33
+
34
+ **Debug approach:**
35
+ 1. Identify async operations
36
+ 2. Find timing dependencies
37
+ 3. Add delays or force ordering to confirm
38
+
39
+ **Fix patterns:**
40
+ - Use async/await properly
41
+ - Add proper synchronization
42
+ - Consider immutable state
43
+
44
+ ---
45
+
46
+ ## Logic Errors
47
+
48
+ **Symptoms:** Wrong output, wrong behaviour
49
+
50
+ **Debug approach:**
51
+ 1. Trace through the logic manually
52
+ 2. Compare expected vs actual at each step
53
+ 3. Find the first step that diverges
54
+
55
+ **Fix patterns:**
56
+ - Fix the condition
57
+ - Correct the calculation
58
+ - Update the state correctly
59
+
60
+ ---
61
+
62
+ ## Performance Issues
63
+
64
+ **Symptoms:** Slow, memory leaks, timeouts
65
+
66
+ **Debug approach:**
67
+ 1. Measure first (don't guess)
68
+ 2. Identify hot paths
69
+ 3. Optimize the bottleneck
70
+
71
+ **See also:** Phase 4 "Perf branch" in SKILL.md for the disciplined approach.
@@ -13,8 +13,8 @@
13
13
  **Do:**
14
14
  - Print at function entry/exit to trace execution
15
15
  - Print variable values at key decision points
16
- - Use descriptive labels: console.log('userId:', userId)
17
- - Print full objects for inspection: console.log(JSON.stringify(data, null, 2))
16
+ - Use descriptive labels: `console.log('userId:', userId)`
17
+ - Print full objects for inspection: `console.log(JSON.stringify(data, null, 2))`
18
18
 
19
19
  **Don't:**
20
20
  - Leave print statements in production code
@@ -22,9 +22,25 @@
22
22
  - Over-print (noise obscures signal)
23
23
  - Print in tight loops (use breakpoints instead)
24
24
 
25
- ### Structured Logging
25
+ ### Tagged Debug Logs
26
26
 
27
- Better than console.log for production:
27
+ When instrumenting during a debugging session, tag every temporary log with a
28
+ unique prefix so cleanup is trivial:
29
+
30
+ ```javascript
31
+ console.log('[DEBUG-a4f2] userId:', userId);
32
+ console.log('[DEBUG-a4f2] payload:', payload);
33
+ ```
34
+
35
+ Cleanup: `grep -r '\[DEBUG-' .` → delete all matches.
36
+
37
+ Untagged logs survive; tagged logs die.
38
+
39
+ ---
40
+
41
+ ## Structured Logging
42
+
43
+ Better than `console.log` for production:
28
44
 
29
45
  ```javascript
30
46
  const logger = {
@@ -115,3 +131,27 @@ logger.error('Request failed', {
115
131
  | INFO | Normal operation events |
116
132
  | WARN | Something unexpected, but handled |
117
133
  | ERROR | Operation failed |
134
+
135
+ ---
136
+
137
+ ## Performance Regression Techniques
138
+
139
+ ### The Perf Branch
140
+
141
+ For performance regressions, logs are usually wrong. Instead:
142
+
143
+ 1. **Establish a baseline measurement** using one of:
144
+ - Timing harness around the suspect code path
145
+ - `performance.now()` in browser / `process.hrtime()` in Node
146
+ - CPU profiler (Chrome DevTools, `node --prof`, py-spy, etc.)
147
+ - Database query planner (`EXPLAIN ANALYZE`)
148
+
149
+ 2. **Bisect** — narrow down which commit / change introduced the regression.
150
+
151
+ 3. **Measure first, fix second.** Do not optimise without data.
152
+
153
+ ### Hot Path Identification
154
+
155
+ - Profile before guessing
156
+ - Look for O(n²) algorithms, unnecessary renders, blocking operations
157
+ - Check for N+1 queries, unbounded caches, recursive without memoisation
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: code-handoff
3
+ description: Compact the current conversation into a handoff document for another agent to pick up.
4
+ argument-hint: "What will the next session be used for?"
5
+ user-invocable: true
6
+ ---
7
+
8
+ Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace.
9
+
10
+ Include a "suggested skills" section in the document, which suggests skills that the agent should invoke.
11
+
12
+ Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.
13
+
14
+ Redact any sensitive information, such as API keys, passwords, or personally identifiable information.
15
+
16
+ If the user passed arguments, treat them as a description of what the next session will focus on and tailor the doc accordingly.
@@ -97,8 +97,6 @@ Output findings in this format:
97
97
  - **Pre-existing**: Yes/No (check if existed before this PR)
98
98
  ```
99
99
 
100
- Loads: `code-security` (if exists)
101
-
102
100
  ---
103
101
 
104
102
  ### Performance Reviewer
@@ -128,8 +126,6 @@ Output findings in this format:
128
126
  - **Pre-existing**: Yes/No
129
127
  ```
130
128
 
131
- Loads: `code-perf` (if exists)
132
-
133
129
  ---
134
130
 
135
131
  ### Correctness Reviewer
@@ -532,8 +528,6 @@ Each reviewer should load relevant skills before reviewing:
532
528
 
533
529
  | Reviewer Type | Skills to Load |
534
530
  |---------------|----------------|
535
- | Security | `code-security` |
536
- | Performance | `code-perf` |
537
531
  | Correctness | Language-specific (`typescript-testing`, `python-testing`, etc.) |
538
532
  | Maintainability | Testing skills |
539
533
  | Architecture | `oracle-architect` |
@@ -580,10 +574,9 @@ prompt: |
580
574
 
581
575
  YOUR FIRST TASK - LOAD SKILLS:
582
576
  As a Security Reviewer, you should load relevant skills before reviewing:
583
- 1. Load `skill: code-security` - Use the `skill` tool
584
- 2. Load `skill: typescript-testing` (if available) - Use the `skill` tool
577
+ 1. Load `skill: typescript-testing` (if available) - Use the `skill` tool
585
578
 
586
- These skills contain security patterns and TypeScript testing guidance that will help you identify issues.
579
+ This skill contains TypeScript testing guidance that will help you identify issues.
587
580
 
588
581
  Focus areas after loading skills:
589
582
  - Authentication and authorization flaws
@@ -663,8 +656,7 @@ prompt: |
663
656
 
664
657
  YOUR FIRST TASK - LOAD SKILLS:
665
658
  As a PerformanceOperator Reviewer, you should load relevant skills before reviewing:
666
- 1. Load `skill: code-perf` (if available) - Use the `skill` tool
667
- 2. Load `skill: typescript-testing` (if available) - Use the `skill` tool
659
+ 1. Load `skill: typescript-testing` (if available) - Use the `skill` tool
668
660
 
669
661
  Focus areas after loading skills:
670
662
  - Performance issues that matter in prod
@@ -62,8 +62,7 @@ prompt: |
62
62
  4. **Identify skills to load** based on detected language/framework:
63
63
  - TypeScript → typescript-testing, typescript-fastify (if fastify framework)
64
64
  - Python → python-testing, python-fastapi (if fastapi framework)
65
- - Security concerns → code-security
66
- - Performance concerns → code-perf
65
+
67
66
 
68
67
  Return ONLY valid JSON (no markdown, no code blocks):
69
68
  {
@@ -73,7 +72,7 @@ prompt: |
73
72
  "domain": "web-api|frontend|database|..."
74
73
  },
75
74
  "reviewers": ["Security", "Correctness"],
76
- "skills_to_load": ["typescript-testing", "code-security"]
75
+ "skills_to_load": ["typescript-testing"]
77
76
  }
78
77
  ```
79
78
 
@@ -83,7 +82,7 @@ prompt: |
83
82
  {
84
83
  "context": { "language": "typescript", "framework": "fastify", "domain": "web-api" },
85
84
  "reviewers": ["Security", "Correctness", "PerformanceOperator"],
86
- "skills_to_load": ["typescript-testing", "code-security"]
85
+ "skills_to_load": ["typescript-testing"]
87
86
  }
88
87
  ```
89
88
 
@@ -102,7 +101,7 @@ prompt: |
102
101
  Before dispatching reviewers, the detected skills are passed to each reviewer:
103
102
  ```json
104
103
  {
105
- "skills_to_load": ["typescript-testing", "code-security"]
104
+ "skills_to_load": ["typescript-testing"]
106
105
  }
107
106
  ```
108
107
 
@@ -6,6 +6,24 @@ user-invocable: false
6
6
 
7
7
  # Architect Skill
8
8
 
9
+ ## Glossary
10
+
11
+ Precise vocabulary for every architectural decision. Use these terms exactly — consistency is the point.
12
+
13
+ - **Module** — anything with an interface and an implementation (function, class, package, layer). Router, Service, Entity, and Repository are all modules.
14
+ - **Interface** — everything a caller must know to use the module: types, invariants, error modes, ordering, config. Not just the type signature.
15
+ - **Seam** — where a module's interface lives; a place behaviour can be altered without editing in place. The functional core / effectful edge boundary is the primary seam.
16
+ - **Depth** — leverage at the interface. A module is **deep** when a lot of behaviour sits behind a small interface. A module is **shallow** when the interface is nearly as complex as the implementation.
17
+ - **Adapter** — a concrete thing satisfying an interface at a seam. A Postgres repository is an adapter; an in-memory fake for testing is another adapter at the same seam.
18
+ - **Leverage** — what callers get from depth: more capability per unit of interface they learn.
19
+ - **Locality** — what maintainers get from depth: change, bugs, and knowledge concentrate in one place.
20
+
21
+ Key principles (apply to every decision):
22
+
23
+ - **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.
24
+ - **The interface is the test surface.** Callers and tests cross the same seam. If you want to test past the interface, the module is probably the wrong shape.
25
+ - **One adapter = hypothetical seam. Two adapters = real seam.** Don't introduce a seam unless something actually varies across it.
26
+
9
27
  Domain-Driven Design and hexagonal architecture with functional core pattern for feature design.
10
28
 
11
29
  ## Architecture Model
@@ -25,6 +43,68 @@ Unified view of functional core and effectful edge:
25
43
 
26
44
  **Key Principle:** Business logic lives in the functional core (Service + Entity). IO operations live in the effectful edge. Core defines interfaces; edge implements them (dependency inversion).
27
45
 
46
+ ## Evaluating Architecture Decisions
47
+
48
+ Before creating a new module, run these checks:
49
+
50
+ ### Deletion Test
51
+
52
+ > *Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.*
53
+
54
+ **When to apply:** Before extracting a new Service, Repository, or utility class.
55
+
56
+ **Example:**
57
+ ```
58
+ ❌ Shallow: OrderFormatterService with 1 method per field
59
+ → Deleting it just moves 6 lines into the Router
60
+ → Interface is as complex as implementation
61
+
62
+ ✅ Deep: OrderService with validate → reserve → save → publish
63
+ → Deleting it replicates orchestration across N handlers
64
+ → Small interface (createOrder), large implementation
65
+ ```
66
+
67
+ ### Interface as Test Surface
68
+
69
+ > *Callers and tests cross the same seam. If you want to test past the interface, the module is probably the wrong shape.*
70
+
71
+ **Implication:**
72
+ - Unit tests for Entities test the public interface (validate, transform)
73
+ - Unit tests for Services test through the seam with stub repositories
74
+ - Integration tests for Repositories test the actual adapter
75
+ - If you find yourself testing "internal" methods, the module needs redrawing
76
+
77
+ ### Adapter Rule
78
+
79
+ > *One adapter = hypothetical seam. Two adapters = real seam.*
80
+
81
+ **When to apply:** Deciding whether to extract an interface.
82
+
83
+ **Examples:**
84
+ ```typescript
85
+ // ❌ Hypothetical seam — only one adapter exists
86
+ interface IEmailClient { send(email: Email): Promise<void>; }
87
+ class SendgridClient implements IEmailClient { ... }
88
+ // No second adapter. The interface adds indirection without value.
89
+
90
+ // ✅ Real seam — two adapters exist
91
+ interface IOrderRepository { save(order: Order): Promise<Order>; }
92
+ class PostgresOrderRepository implements IOrderRepository { ... }
93
+ class InMemoryOrderRepository implements IOrderRepository { ... } // for tests
94
+ // The seam earns its keep because callers vary (production vs test).
95
+ ```
96
+
97
+ ### Depth Check
98
+
99
+ Prefer deep modules over shallow ones:
100
+
101
+ | Module | Interface Size | Implementation | Depth |
102
+ |--------|--------------|----------------|-------|
103
+ | `OrderValidator` | 3 methods (validate, validateItems, validateAddress) | 30 lines each | Shallow |
104
+ | `Order` (entity) | 4 methods (fromRequest, toRecord, toResponse, validate) | 200 lines of rules, transforms, invariants | **Deep** |
105
+
106
+ **Rule of thumb:** A module's interface should hide at least 3x the complexity it exposes.
107
+
28
108
  ## Functional Core
29
109
 
30
110
  Pure, deterministic components containing all business logic.
@@ -241,6 +321,7 @@ The testing skill uses architectural structure to determine:
241
321
 
242
322
  For detailed patterns and examples:
243
323
 
244
- - **See [references/ddd-patterns.md](references/ddd-patterns.md)** - Aggregates, Value Objects, Domain Events, Bounded Contexts, composition patterns
324
+ - **See [references/ddd-patterns.md](references/ddd-patterns.md)** - Aggregates, Value Objects, Domain Events, Bounded Contexts, composition patterns, seam placement
245
325
  - **See [references/data-modeling.md](references/data-modeling.md)** - Entity design principles, schema patterns, access pattern optimization, data transformation
246
326
  - **See [references/api-design.md](references/api-design.md)** - REST conventions, request/response contracts, error handling, versioning patterns
327
+ - **See [references/interface-design.md](references/interface-design.md)** - Designing module interfaces, "Design It Twice" pattern, depth heuristics, dependency categories, anti-patterns
@@ -245,3 +245,104 @@ Bounded Context: "Orders"
245
245
  3. **Aggregate → Repository**: Service uses repository to persist aggregate
246
246
  4. **Service → Producer**: Service publishes domain events after state change
247
247
  5. **Consumer → Service**: Events from other contexts trigger service operations
248
+
249
+ ## Seam Placement
250
+
251
+ Where you put the seam matters as much as what goes behind it.
252
+
253
+ ### Core/Edge Seam
254
+
255
+ The primary seam runs between the functional core and the effectful edge:
256
+
257
+ ```
258
+ ┌─────────────────────────────────┐
259
+ │ Functional Core (pure) │
260
+ │ ┌──────────────────────────┐ │
261
+ │ │ OrderService │ │
262
+ │ │ createOrder() ────────┼───┼──▶ (seam)
263
+ │ │ processPayment() │ │
264
+ │ └──────────────────────────┘ │
265
+ └─────────────────────────────────┘
266
+ │
267
+ ▼ seam
268
+ ┌─────────────────────────────────┐
269
+ │ Effectful Edge (IO) │
270
+ │ ┌──────────────────────────┐ │
271
+ │ │ PostgresOrderRepository │◀──┼── adapter
272
+ │ │ HttpPaymentClient │ │ adapter
273
+ │ │ InMemoryOrderRepository │ │ adapter (test)
274
+ │ └──────────────────────────┘ │
275
+ └─────────────────────────────────┘
276
+ ```
277
+
278
+ **Rules:**
279
+ - Core defines the interface; edge implements it (dependency inversion)
280
+ - The seam must have **at least two adapters** to be real (production + test)
281
+ - Core never imports edge code; edge imports core interfaces
282
+
283
+ ### Internal Seams
284
+
285
+ A deep module can have **internal seams** — private to its implementation, used by its own tests:
286
+
287
+ ```typescript
288
+ class Order {
289
+ // External seam (public interface)
290
+ validate(): Result<Order> { ... }
291
+
292
+ // Internal seam (private, tested directly)
293
+ private calculateTax(subtotal: number): Money { ... }
294
+ }
295
+ ```
296
+
297
+ Internal seams let you test complex logic without exposing it in the public interface.
298
+
299
+ ### When to Create a Seam
300
+
301
+ Use the **adapter rule**: one adapter = hypothetical; two = real.
302
+
303
+ | Scenario | Action |
304
+ |----------|--------|
305
+ | One database implementation, no test fake | No seam needed. Direct dependency is simpler. |
306
+ | One database implementation, test fake exists | Extract interface. The fake is the second adapter. |
307
+ | Multiple database implementations (Postgres + DynamoDB) | Extract interface. The implementations are the adapters. |
308
+ | External API with one client | Direct dependency. Introduce seam only when you need a mock/stub for testing. |
309
+
310
+ ### Anti-Patterns
311
+
312
+ **Seam at every dependency:**
313
+ ```typescript
314
+ // ❌ Too many seams — every dependency gets an interface
315
+ interface IValidator { ... }
316
+ interface IFormatter { ... }
317
+ interface IConverter { ... }
318
+ // Each has one adapter. Indirection without value.
319
+ ```
320
+
321
+ **No seam at the core/edge boundary:**
322
+ ```typescript
323
+ // ❌ Core imports edge directly
324
+ import { db } from '../edge/postgres';
325
+ class OrderService {
326
+ async createOrder(req) {
327
+ await db.orders.create(...); // Core knows about Postgres
328
+ }
329
+ }
330
+ ```
331
+
332
+ **Leaky seam:**
333
+ ```typescript
334
+ // ❌ Interface exposes implementation details
335
+ interface OrderRepository {
336
+ queryRaw(sql: string): Promise<Row[]>; // Caller knows SQL
337
+ beginTransaction(): Promise<Transaction>; // Caller manages transactions
338
+ }
339
+ // The seam leaks. Callers become coupled to the database.
340
+ ```
341
+
342
+ ### Seam Placement Checklist
343
+
344
+ Before introducing a new seam, verify:
345
+ - [ ] At least two adapters will exist (production + test, or multiple production)
346
+ - [ ] The interface hides implementation details (no SQL, no HTTP, no framework types)
347
+ - [ ] The core doesn't import edge code
348
+ - [ ] Deleting the module would re-appear complexity across N callers
@@ -0,0 +1,203 @@
1
+ # Interface Design Reference
2
+
3
+ Patterns for designing module interfaces that maximize depth, leverage, and locality.
4
+
5
+ Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best.
6
+
7
+ ## Design It Twice
8
+
9
+ Before committing to a module's interface, sketch at least two alternatives with different trade-offs.
10
+
11
+ ### Process
12
+
13
+ 1. **Frame the problem space**
14
+ - List constraints any interface must satisfy
15
+ - Identify dependencies and which category they fall into
16
+ - Write a rough illustrative code sketch to ground the constraints
17
+
18
+ 2. **Sketch alternatives**
19
+ - Alternative A: Minimize the interface (1–3 entry points max)
20
+ - Alternative B: Maximize flexibility (support many use cases)
21
+ - Alternative C: Optimize for the most common caller (default case trivial)
22
+
23
+ 3. **Compare by depth**
24
+ - Which alternative has the smallest interface for the most behaviour?
25
+ - Which concentrates the most knowledge in one place?
26
+ - Which has the best test surface?
27
+
28
+ ### Example: Order Creation Interface
29
+
30
+ **Problem:** Design the interface for creating an order.
31
+
32
+ **Alternative A — Minimal interface:**
33
+ ```typescript
34
+ interface OrderService {
35
+ createOrder(request: CreateOrderRequest): Promise<Result<Order>>;
36
+ }
37
+ ```
38
+ - **Pros:** Tiny surface. One seam. Easy to mock.
39
+ - **Cons:** All complexity hidden inside. Hard to test partial flows (e.g. validation without persistence).
40
+ - **Depth:** Very deep. High leverage, high locality.
41
+
42
+ **Alternative B — Flexible interface:**
43
+ ```typescript
44
+ interface OrderService {
45
+ validate(request: CreateOrderRequest): Promise<Result<Order>>;
46
+ reserveInventory(order: Order): Promise<Result<Order>>;
47
+ save(order: Order): Promise<Result<Order>>;
48
+ publishEvents(order: Order): Promise<void>;
49
+ }
50
+ ```
51
+ - **Pros:** Callers compose their own flows. Easy to test each step.
52
+ - **Cons:** Interface is as complex as implementation. Callers must know ordering constraints (reserve before save).
53
+ - **Depth:** Shallow. Low leverage — callers replicate orchestration.
54
+
55
+ **Alternative C — Optimized for common case:**
56
+ ```typescript
57
+ interface OrderService {
58
+ createOrder(request: CreateOrderRequest): Promise<Result<Order>>;
59
+ createOrderDraft(request: CreateOrderRequest): Promise<Result<Order>>; // no inventory check
60
+ confirmOrder(orderId: string): Promise<Result<Order>>; // draft → confirmed
61
+ }
62
+ ```
63
+ - **Pros:** Common case (createOrder) is trivial. Draft pattern supports async workflows.
64
+ - **Cons:** More surface area than Alternative A. Callers must understand draft vs confirmed.
65
+ - **Depth:** Moderate. Good leverage for the common case.
66
+
67
+ **Recommendation:** Alternative C. It balances depth with real workflow needs. The draft/confirm split is a real seam (two callers: interactive vs batch).
68
+
69
+ ## Interface Size Heuristics
70
+
71
+ ### The Rule of Three
72
+
73
+ A module's public interface should have no more than 3 primary entry points for each responsibility:
74
+
75
+ ```typescript
76
+ // ❌ Too wide
77
+ interface OrderService {
78
+ createOrder(...): Promise<Order>;
79
+ updateOrder(...): Promise<Order>;
80
+ deleteOrder(...): Promise<void>;
81
+ getOrder(...): Promise<Order>;
82
+ listOrders(...): Promise<Order[]>;
83
+ searchOrders(...): Promise<Order[]>;
84
+ cancelOrder(...): Promise<Order>;
85
+ shipOrder(...): Promise<Order>;
86
+ refundOrder(...): Promise<Order>;
87
+ }
88
+
89
+ // ✅ Split by responsibility (2 modules, each with ~3 methods)
90
+ interface OrderLifecycleService {
91
+ create(...): Promise<Order>;
92
+ cancel(...): Promise<Order>;
93
+ ship(...): Promise<Order>;
94
+ }
95
+
96
+ interface OrderQueryService {
97
+ get(id: string): Promise<Order>;
98
+ list(filters: OrderFilters): Promise<Order[]>;
99
+ search(query: string): Promise<Order[]>;
100
+ }
101
+ ```
102
+
103
+ ### Invariants in the Interface
104
+
105
+ The interface includes not just types, but everything a caller must know:
106
+
107
+ ```typescript
108
+ interface PaymentService {
109
+ // ❌ Interface hides invariants
110
+ processPayment(orderId: string, amount: number): Promise<Receipt>;
111
+ // Caller must discover: amount must match order total, order must be confirmed,
112
+ // can't process twice, throws if payment gateway is down.
113
+
114
+ // ✅ Interface encodes invariants in types
115
+ processPayment(
116
+ order: ConfirmedOrder, // type encodes "must be confirmed"
117
+ payment: PaymentMethod // type encodes valid methods
118
+ ): Promise<Result<Receipt, PaymentError>>;
119
+ // Error mode explicit in return type. Ordering implicit in input types.
120
+ ```
121
+
122
+ ## Dependency Categories
123
+
124
+ The shape of an interface depends on what it depends on.
125
+
126
+ | Category | Description | Interface Impact |
127
+ |----------|-------------|------------------|
128
+ | **In-process** | Same memory space, no serialization | Can pass rich types, callbacks, streams |
129
+ | **Local-substitutable** | Same interface, different implementations (real vs test) | Seam is real. Interface must hide implementation |
130
+ | **Ports & adapters** | Crosses a real boundary (DB, HTTP, message queue) | Interface must be narrow. Errors are part of the contract |
131
+ | **Mock** | Test-only dependency, no production equivalent | Don't create a seam. Test the real module |
132
+
133
+ ## Common Interface Anti-Patterns
134
+
135
+ ### Leaky Abstraction
136
+
137
+ ```typescript
138
+ // ❌ Interface exposes implementation
139
+ interface UserRepository {
140
+ query(sql: string, params: any[]): Promise<Row[]>;
141
+ }
142
+
143
+ // ✅ Interface hides implementation
144
+ interface UserRepository {
145
+ findByEmail(email: Email): Promise<User | null>;
146
+ save(user: User): Promise<User>;
147
+ }
148
+ ```
149
+
150
+ ### Pass-Through Interface
151
+
152
+ ```typescript
153
+ // ❌ Every method delegates 1:1 to another module
154
+ class OrderService {
155
+ async findById(id: string) { return this.repo.findById(id); }
156
+ async save(order: Order) { return this.repo.save(order); }
157
+ async delete(id: string) { return this.repo.delete(id); }
158
+ }
159
+ // Deletion test: delete OrderService → complexity doesn't reappear.
160
+ // It's a pass-through. Remove it and call repo directly.
161
+ ```
162
+
163
+ ### God Interface
164
+
165
+ ```typescript
166
+ // ❌ One module does everything
167
+ interface PlatformService {
168
+ createUser(...): Promise<User>;
169
+ sendEmail(...): Promise<void>;
170
+ processPayment(...): Promise<Receipt>;
171
+ generateReport(...): Promise<PDF>;
172
+ }
173
+ // No cohesion. Deleting it would reappear complexity, but in unrelated places.
174
+ // Split by domain responsibility instead.
175
+ ```
176
+
177
+ ### Chatty Interface
178
+
179
+ ```typescript
180
+ // ❌ Many small methods that must be called in sequence
181
+ interface OrderBuilder {
182
+ setCustomer(customerId: string): this;
183
+ addItem(item: OrderItem): this;
184
+ setShipping(address: Address): this;
185
+ setPayment(method: PaymentMethod): this;
186
+ validate(): Result<void>;
187
+ build(): Order;
188
+ }
189
+ // Callers must know ordering. State machine hidden in method calls.
190
+ // Better: single method that takes a complete request.
191
+ ```
192
+
193
+ ## Decision Checklist
194
+
195
+ Before finalizing an interface:
196
+
197
+ - [ ] Run the deletion test — does complexity reappear across N callers?
198
+ - [ ] Check depth — is the interface at least 3x smaller than the implementation?
199
+ - [ ] Verify seam — are there at least two adapters?
200
+ - [ ] Encode invariants — are error modes, ordering, and constraints visible in types?
201
+ - [ ] Test surface — can you test all behaviour through the public interface?
202
+ - [ ] Hide details — does the interface leak SQL, HTTP, frameworks, or implementation?
203
+ - [ ] Common case — is the most frequent caller's experience trivial?