docs(AGENTS): tighten YAGNI discipline, resolve value-object contradiction
- Clarify 'value objects over primitives' is reactive, not anticipatory - Mark the Step 3 example as aspirational domain shape, not per-story template - Add 'What to skip per story' trap table with concrete boundaries - Add litmus test: 'point to a failing property or don't write it'
This commit is contained in:
parent
e05572bcec
commit
5a5e06f471
31
AGENTS.md
31
AGENTS.md
@ -62,7 +62,7 @@ fc.property(
|
|||||||
|
|
||||||
### Step 3: "I can't believe it's not Haskell"
|
### Step 3: "I can't believe it's not Haskell"
|
||||||
|
|
||||||
Use TypeScript's type system to encode domain constraints. Add only code necessary to make the property pass, no more:
|
Use TypeScript's type system to encode domain constraints. **The example below shows the complete domain shape — not what to implement per story.** Implement only what a property forces you to write.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ADTs via discriminated unions
|
// ADTs via discriminated unions
|
||||||
@ -107,20 +107,37 @@ class Character {
|
|||||||
**Key principles:**
|
**Key principles:**
|
||||||
|
|
||||||
- **ADTs over classes** — use discriminated unions for state/variants
|
- **ADTs over classes** — use discriminated unions for state/variants
|
||||||
- **Value objects over primitives** — `Health`, `Damage`, `Level` instead of `number`
|
- **Value objects over primitives** — when a property reveals that a bare `number` or `string` is insufficient, introduce a value object. Don't anticipate — react.
|
||||||
- **Immutability** — no `this.health = ...`, return new instances
|
- **Immutability** — no `this.health = ...`, return new instances
|
||||||
- **Invariants at boundaries** — constructors enforce invariants, not getters/setters
|
- **Invariants at boundaries** — constructors enforce invariants, not getters/setters
|
||||||
- **Pure functions** — domain logic has no side effects, testable in isolation
|
- **Pure functions** — domain logic has no side effects, testable in isolation
|
||||||
- **YAGNI** Write the minimum code necessary to make the current property pass.
|
|
||||||
|
|
||||||
Before writing a method, ask:
|
**YAGNI discipline:** Write only the minimum code necessary to make the current property pass.
|
||||||
|
|
||||||
1. Does a story property require this? If no → don't write it.
|
Before writing a method or class, ask:
|
||||||
2. Does this method touch a concept from a different story? If yes → it's scope creep.
|
|
||||||
3. Am I implementing something because it feels useful, not because a property forces it? If yes → stop.
|
1. **Does a story property require this?** If no → don't write it.
|
||||||
|
2. **Does this touch a concept from a different story?** If yes → it's scope creep.
|
||||||
|
3. **Am I implementing this because it feels useful, not because a property forces it?** If yes → stop.
|
||||||
|
|
||||||
|
> **The litmus test:** If you can't point to a failing property that demands this code, don't write it. Future stories will reveal what abstractions are actually needed — and they'll look different than you expect.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### What to skip per story (common traps)
|
||||||
|
|
||||||
|
| Story | Don't implement | Belongs to |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 (Creation) | `isAllyOf`, `isAlive`, `isDead` | Stories 3+ |
|
||||||
|
| 1 (Creation) | `Health.add()`, `Health.isMax()` | Stories 3/4 |
|
||||||
|
| 1 (Creation) | `Level.next()`, `Level.diff()` | Story 5 |
|
||||||
|
| 2 (Damage) | `Damage` value object | Only if a property demands it |
|
||||||
|
| 2 (Damage) | Level modifier (±50%) | Story 3 |
|
||||||
|
| 2 (Damage) | Faction/ally checks | Story 3 |
|
||||||
|
| 3 (Levels) | MagicalObjects | Story 4 |
|
||||||
|
| 3 (Levels) | Level-up tracking | Story 5 |
|
||||||
|
| 4 (Objects) | `joinFaction` / `leaveFaction` | Story 3 |
|
||||||
|
|
||||||
### Example: Damage Property
|
### Example: Damage Property
|
||||||
|
|
||||||
From user story: _"When damage received exceeds current Health, Health becomes 0 and the character dies"_
|
From user story: _"When damage received exceeds current Health, Health becomes 0 and the character dies"_
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user