# Documentation Examples

Good vs bad examples showing the key differences.

---

## Example 1: Function Documentation

### ❌ Bad: Full Implementation

```markdown
### mergeData

function mergeData(server, local, changes) { const result = {}; const serverMap = new Map(server.map((i) => [i.id, i])); // ... 30 more lines return result; }
```

**Problems:** Will become obsolete, focuses on HOW not WHAT.

### ✅ Good: Contract Only

```markdown
### `mergeData(serverData, localData, changes)`

**Goal:** Merge server data with local changes

**Input:**

- `serverData` (array) - Fresh from API
- `localData` (array) - Current state
- `changes` (object) - `{ added, updated, removed }`

**Output:** `{ merged: array, conflicts: array }`

**Merge Rules:**

- Server wins for conflicts
- Local additions preserved
- Local removals marked as intent
```

---

## Example 2: State Documentation

### ❌ Bad: State Everywhere

```markdown
## Component State

const [items, setItems] = useState([]);

## Hook State

{ data: items, isLoading: loading, error: null }

## Redux State

{ items: [], loading: false, error: null }
```

**Problems:** Documents transient state, repeats shapes.

### ✅ Good: Persistence Points Only

```markdown
## State

**Frontend (at persistence):**

{
  "data": [],
  "changes": { "added": [], "updated": [], "removed": [] }
  // "cache": {}  // UNUSED: dead key; kept so old persisted state rehydrates
}

**Storage:** localStorage `app_state`

**Backend:**

export const items = pgTable('items', {
  id: text('id').primaryKey(),
  data: jsonb('data'),
});
```

---

## Example 3: Changelog

### ❌ Bad: Vague

```markdown
# 2025-01-05 - Updates

## Changes

- Updated editor
- Fixed bugs
- Improved performance

## Files

- Various files in src/
```

### ✅ Good: Function-Level Detail

```markdown
# 2025-01-05 - Version-Based Merge

## Summary

Replaced change-tracking merge with version-based conflict resolution.

## Changes

### Added

- `mergeAndDerive(server, local, versionsMatch, type)` in `src/utils/merge.js`
  - Purpose: Single-pass merge with change detection
  - Returns: `{ merged, changes }`

### Changed

- `useDataSync()` in `src/hooks/useDataSync.js`
  - Changed: Now compares versions before merge

### Removed

- `deriveChanges()` from `src/utils/derive.js`
  - Replaced by: Logic in `mergeAndDerive()`

## Breaking Changes

⚠️ **Merge behavior changed**

- Old: Local changes always preserved
- New: Local preserved only if versions match

## Files Changed

- `src/hooks/useDataSync.js`
- `src/utils/merge.js`
- `src/utils/derive.js` (deleted)
```

---

## Example 4: Data Flow

### ❌ Bad: Prose Description

```markdown
When the page loads, the useAppInit hook calls the syncData function. This function first fetches data from the server using the API. Then it gets the local data from Redux. After that, it compares versions...
```

### ✅ Good: Diagram + Key Points

```markdown
## Data Flow

┌─────────────────────────────────────────┐
│               DATA SYNC                 │
├─────────────────────────────────────────┤
│                                         │
│   ┌──────┐     ┌──────┐     ┌──────┐    │
│   │Server│────▶│Merge │────▶│Redux │    │
│   │ API  │     │      │     │Store │    │
│   └──────┘     └──────┘     └──────┘    │
│        │                                │
│        ▼                                │
│   Version Check                         │
│                                         │
└─────────────────────────────────────────┘

**Trigger:** `useAppInit()` on page load

**Version Rule:** Match → local wins, Mismatch → server wins
```

---

## Example 5: Architecture Decision revision

### ❌ Bad: Supersession chain

```
docs/architecture/decisions/
├── AD-002-auth-keycloak.md        (status: Superseded)
├── AD-006-auth-better-auth.md     (status: Superseded, "supersedes AD-002")
└── AD-011-auth-better-auth-v2.md  (status: Accepted, "supersedes AD-006")
```

**Problems:** Three files hold one subject; the reader must diff a chain to learn the current truth; two of the three are wrong-but-present (the same pathology as keeping `report_v1.sql` beside the live `report_v2.sql`); the reasoning that killed Keycloak is stranded in a dead file nobody opens.

### ✅ Good: One subject, updated in place

```
docs/architecture/decisions/
└── AD-002-auth.md                 (status: Accepted, last-updated bumped)
```

```markdown
## Decision

We use Better Auth with one-shot credential issuance…   ← current truth, no journey

…

## Decision Changelog

- 2026-05-02 — Moved issuance to one-shot tokens because refresh-token theft
  surfaced in review. Implemented by [plan](…). (Previously: long-lived refresh tokens.)
- 2026-03-18 — Replaced Keycloak with Better Auth because self-hosting cost
  outweighed SSO needs. Implemented by [plan](…). (Previously: Keycloak.)
- 2026-01-10 — Initial decision accepted. Implemented by [plan](…).
```

**Why it works:** The body answers "how does auth work and why" without archaeology; the tail preserves every reversal with its trigger; the rejected alternatives stay findable in the one file a reader actually opens.

---

## Example 6: Stripping history from a truth doc

The move agents fumble: deleting narration is only half the job — the story must land in a record.

### ❌ Bad: The journey narrated in place

```markdown
## Export Pipeline

The exporter was extracted from the legacy `reports` worker (2026-06-02) and
now lives in `src/export/`. Phases 0–2 of the rewrite are complete; streaming
output is not yet implemented. Treat any reference to `reports/export.js` as
stale — it was superseded by `src/export/run.js`.
```

### ✅ Good: Present-tense truth doc + a dated record carrying the story

The feature doc states only what is:

```markdown
## Export Pipeline

`src/export/run.js` renders every export format from one job queue. Output is
buffered per job (streaming is out of scope — see `docs/history/backlog.md`).
```

And the change-story goes to `docs/history/changelog/2026-06-02-export-extraction.md`:

```markdown
# 2026-06-02 — Export pipeline extracted

## Summary

Extracted export logic from the `reports` worker into `src/export/`.

## Changes

### Removed

- `reports/export.js` — replaced by `src/export/run.js`; all callers repointed.
```

**Why it works:** the truth doc needs no archaeology to act on; the dates, the predecessor, and the supersession live in a frozen record; the unbuilt part is a backlog pointer, not a "not yet" hedge; and no stale-reference tripwire survives because the references themselves were fixed.

---

## Quick Comparison

| Aspect     | Bad                  | Good                        |
| ---------- | -------------------- | --------------------------- |
| Code       | Full implementations | Signatures + contracts      |
| State      | Every occurrence     | Persistence points only     |
| Flows      | Prose paragraphs     | ASCII diagrams              |
| Changelogs | "Updated X"          | Function names + migrations |
| Decisions  | New doc per revision | One AD per subject, updated in place |
| History    | Narrated in the doc  | Present-tense doc + dated record |
| Length     | 100+ lines           | 20-40 lines                 |
