# Anti-Patterns & Learnings from DSS Implementation

**Version 1.0.0** | Last Updated: 2025-12-08 | Status: Production

This document catalogs **what not to do** and lessons learned from DSS implementation. When you see these patterns, they're usually violations of one or more core principles.

---

## Anti-Pattern 1: Shadow Config

**What It Is**: Configuration that lives in multiple places (code comments, README, config file) with different values.

**Why It's Bad**: Breaks **Principle 3** (Single Source of Truth). Developers don't know which is the actual value.

### Example

```python
# Code comment in middleware.py
timeout = 30  # Timeout is 30 seconds

# docs/README.md
"Configure timeout (default 60 seconds)"

# config.json
{
  "timeout": 90
}

# Which is right? 🤔
```

### Detection

```bash
# Find hardcoded values in code
grep -r "timeout" code/src/ | grep -v config

# Compare with actual config values
grep "timeout" config.json

# Check README for contradictions
grep -i "timeout" README.md
```

### How to Fix

1. **Identify the source of truth**
   - Where should this value live? (config file, environment variable, database)
   - This is now the ONLY place

2. **Update everywhere else to reference it**
   ```python
   # WRONG:
   timeout = 30

   # RIGHT:
   timeout = config.get("timeout", default=30)

   # README:
   "See config.json for timeout (default 30 seconds)"
   ```

3. **Remove copies**
   - Delete hardcoded values from code
   - Delete from comments
   - Leave only reference

### Checklist

- [ ] Identify canonical config location (config file, env, DB)
- [ ] Code loads from canonical location
- [ ] Comments reference canonical source
- [ ] README points to config file, not duplicate values
- [ ] No hardcoded values elsewhere
- [ ] CI check: Detect hardcoded values (grep patterns)

---

## Anti-Pattern 2: Lying Docstring

**What It Is**: Documentation that doesn't match implementation.

**Why It's Bad**: Breaks **Principle 2** (Contracts are Immutable). Code is the real contract; docstring is a lie.

### Example

```python
def sync_tokens():
    """Syncs Figma tokens to project (synchronous operation)"""

    # Actually async now, but docstring wasn't updated
    async def _sync():
        await figma_api.fetch(...)

    asyncio.create_task(_sync())  # Non-blocking, returns immediately
```

### Detection

```bash
# Check function signatures vs docstrings
# Look for mismatches:
# - Says synchronous but returns Promise/Task
# - Says required but marked optional
# - Says returns X but actually returns Y
```

### How to Fix

1. **Update docstring to match code**
   ```python
   def sync_tokens():
       """Syncs Figma tokens to project (async operation).

       Returns:
           asyncio.Task - Handle to running sync operation
       """
   ```

   OR

2. **Update code to match docstring**
   ```python
   def sync_tokens():  # If contract says synchronous
       """Syncs Figma tokens to project (synchronous operation)."""
       result = asyncio.run(_sync())  # Block until done
       return result
   ```

### Checklist

- [ ] Docstring describes actual behavior
- [ ] Parameter types match signature
- [ ] Return type matches what's returned
- [ ] Exceptions match what's thrown
- [ ] Usage examples work as-is
- [ ] CI check: Parse docstrings and compare with signature

---

## Anti-Pattern 3: Ghost Script

**What It Is**: Old deployment/setup script that nobody maintains but everyone fears deleting.

**Why It's Bad**: Breaks **Principle 3** (Single Source of Truth). Unclear if it's still used, causing confusion and maintenance burden.

### Example

```
repo/
├── deploy_v1.sh (2023, obsolete)
├── deploy_v2.sh (2024, semi-used)
├── deploy_v3.sh (2025, current)
└── deploy_test.sh (unknown origin)

Question: Which one do we use? Nobody knows for sure.
```

### Detection

```bash
# Find all deployment scripts
find . -name "*deploy*" -type f

# Check git history: when was it last modified?
git log --oneline -- deploy_v1.sh | head -5

# Check: is it referenced in docs?
grep -r "deploy_v1" docs/

# Check: does CI/CD use it?
grep -r "deploy_v1" .github/workflows/
```

### How to Fix

**Option 1: Archive It**
```bash
mkdir -p archived/scripts/deployment
mv deploy_v1.sh archived/scripts/deployment/2025-12-08_deploy_v1.sh

# Add README explaining why archived
cat > archived/scripts/deployment/README.md <<EOF
# Archived Deployment Scripts

## deploy_v1.sh
Archived: 2025-12-08
Reason: Replaced by deploy_v3.sh (supports containers, faster rollback)
Reference: See DEPLOYMENT_GUIDE.md for current process
EOF
```

**Option 2: Delete It**
```bash
# If truly unused:
git rm deploy_v1.sh
git commit -m "remove: Delete obsolete deploy_v1.sh (replaced by v3)"
```

### Checklist

- [ ] Audit all deployment scripts
- [ ] Identify which are actively used
- [ ] Delete unused scripts OR archive with reason
- [ ] Document which script is current in DEPLOYMENT_GUIDE.md
- [ ] Update CI/CD to use only current version
- [ ] Prevent committing ghost scripts: CI check for script age

---

## Anti-Pattern 4: Wiki Rot

**What It Is**: Markdown files that were never updated after creation. Over time they become stale, misleading documentation.

**Why It's Bad**: Breaks **Principle 3** (Current State Transparency). Readers follow outdated advice.

### Example

```markdown
# DEPLOYMENT_GUIDE_2024.md

Last Updated: 2024-01-15

To deploy:
1. SSH into prod-server-old
2. Run `node deploy.js`
3. Wait 30 minutes
4. Check http://old-dashboard.com/status

<!-- This guide is now completely wrong -->
```

### Detection

```bash
# Find markdown files not updated in 60+ days
find . -name "*.md" -type f -mtime +60 | head -20

# Check git log: when was it last changed?
git log -1 --format="%ai" -- DEPLOYMENT_GUIDE.md

# Look for "Last Updated" field
grep -r "Last Updated" docs/ | awk -F: '{print $NF}' | sort
```

### How to Fix

1. **Review and Update**
   ```bash
   # For each stale doc:
   1. Read it thoroughly
   2. Check if process still accurate
   3. Update with current reality
   4. Change "Last Updated" date
   5. Commit with "docs: Update GUIDE.md for 2025"
   ```

2. **Or Archive It**
   ```bash
   mkdir -p docs/archive/2024
   mv DEPLOYMENT_GUIDE_2024.md docs/archive/2024/

   # Create new one for current year
   cp DEPLOYMENT_GUIDE_TEMPLATE.md DEPLOYMENT_GUIDE.md
   ```

### Checklist

- [ ] All active docs have "Last Updated" field
- [ ] Timestamp is within last 30 days
- [ ] Docs reviewed regularly (monthly audit)
- [ ] Stale docs archived immediately
- [ ] CI check: Fail if doc older than 90 days
- [ ] Process: Every PR that changes process updates related docs

---

## Anti-Pattern 5: Sprint Spec

**What It Is**: Technical specification written during sprint planning, never consolidated into permanent documentation.

**Why It's Bad**: Breaks **Principle 3** (Single Source of Truth). Knowledge lost when sprint ends. Phases become documentation.

### Example

```
Phase 5D - Token Merge System
│
├── SPRINT_15_TECH_SPEC.md (contains API design details)
├── PHASE_5D_PLANNING.md (implementation approach)
├── PHASE_5D_FINAL_REPORT.md (what was actually built)
│
└── docs/01_core/API_CONTRACTS.md (empty—didn't update this!)
```

Problem: If someone asks "what API does token merge use?" the answer is scattered across 3 phase docs, not in the canonical API_CONTRACTS.md.

### Detection

```bash
# Find phase-named files
find . -maxdepth 1 -name "*PHASE*" -o -name "*SPRINT*" -o -name "*WEEK*"

# Check: Are these in root or in archive?
ls -la | grep -i "phase\|sprint\|week"

# Scan for technical specs in phase files
grep -l "endpoint\|API\|schema" PHASE_*.md
```

### How to Fix

1. **Extract Technical Content**
   ```
   OLD (scattered):
     SPRINT_15_TECH_SPEC.md has API design
     ARCHITECTURE.md is generic

   NEW (consolidated):
     ARCHITECTURE.md updated with token merge design
     API_CONTRACTS.md has full endpoint specs
     SPRINT_15_TECH_SPEC.md archived (reference only)
   ```

2. **Extract Decision Rationale**
   ```
   OLD (in phase report):
     "We chose token merge because Figma + CSS coexist"

   NEW (in ADR):
     .knowledge/adrs/ADR-001-token-merge.md
     Context: Why this matters
     Decision: Token merge approach
     Alternatives: What we considered
     Consequences: Trade-offs
   ```

3. **Archive Phase Documents**
   ```bash
   mkdir -p docs/archive/phases/phase-5d
   mv PHASE_5D_PLANNING.md docs/archive/phases/phase-5d/
   mv SPRINT_15_TECH_SPEC.md docs/archive/phases/phase-5d/
   ```

### Checklist

- [ ] All technical specs extracted to Tier 1 (API_CONTRACTS.md, ARCHITECTURE.md)
- [ ] All decision rationale extracted to ADRs
- [ ] All status updates consolidated to PROJECT_STATUS.md
- [ ] Phase docs archived immediately (not left in root)
- [ ] API_CONTRACTS.md updated with all new endpoints
- [ ] CI check: Fail if *PHASE_*.md, *SPRINT_*.md found in root

---

## Anti-Pattern 6: Version Chaos

**What It Is**: Files change without corresponding version bumps, or versions are bumped arbitrarily.

**Why It's Bad**: Breaks **Principle 2** (Contracts are Immutable and Versioned). External systems can't pin to stable versions.

### Example

```
API_CONTRACTS.md:
  v1.0.0 (2025-11-01): Initial version

2025-11-05: "Added timeout to GET /sync" (no version bump!)
2025-11-10: "Changed response schema" (no version bump!)
2025-12-01: "Version 1.5.0" (what changed? no changelog!)

Consumer can't trust version numbers or stable APIs.
```

### Detection

```bash
# Check if version matches commit history
git log --all --oneline -- API_CONTRACTS.md | head -20

# Count changes without version bumps
git diff v1.0.0..HEAD -- API_CONTRACTS.md | wc -l

# Find missing changelogs
ls -la docs/*CHANGELOG* docs/*VERSION*
```

### How to Fix

1. **Establish Versioning Discipline**
   ```
   Every change to Tier 1 files:
   1. Decide: MAJOR, MINOR, or PATCH
   2. Update version in file
   3. Add changelog entry (what, why, migration path)
   4. Commit with message: "v1.2.0: Add new endpoint"
   5. Tag commit: git tag v1.2.0
   ```

2. **Create Changelog**
   ```markdown
   # API_CONTRACTS.md Changelog

   ## v1.2.0 (2025-12-08)

   ### Added
   - GET /api/tokens/resolve endpoint (per-project token lookup)

   ### Changed
   - POST /api/tokens/merge now supports PREFER_SPECIFIC strategy

   ### Deprecated
   - GET /api/v1/projects/{id} (use v2 endpoint instead)

   ### Migration
   See MIGRATION_GUIDE.md for upgrading from v1.1.0
   ```

3. **Establish Review Process**
   ```
   PR template asks:
   - [ ] Version bumped?
   - [ ] Changelog entry?
   - [ ] Breaking changes documented?
   - [ ] Migration path provided?
   ```

### Checklist

- [ ] All Tier 1 files have version numbers
- [ ] Version follows semver (MAJOR.MINOR.PATCH)
- [ ] Every change bumps version
- [ ] Changelog entries for each version
- [ ] Git tags for each release
- [ ] Breaking changes clearly marked
- [ ] Migration guides for breaking changes
- [ ] CI check: Fail if file changed but version didn't

---

## Anti-Pattern 7: Dead Documentation

**What It Is**: Documentation files that reference each other but neither is updated, creating a closed loop of outdated information.

**Why It's Bad**: Breaks **Principle 3** (Single Source of Truth). Difficult to know which is the source.

### Example

```
README.md says:
  "For full details see DEPLOYMENT_GUIDE.md"

DEPLOYMENT_GUIDE.md says:
  "For overview see README.md"

Both are outdated (last updated 2024-06):
  Neither is maintained
  Both reference each other in circles
  Neither is authoritative
```

### Detection

```bash
# Find cross-references between docs
grep -h "See.*\.md\|For.*see" docs/*.md | sort | uniq -c

# Check which docs are circular references
# (if A links to B, B links to A, probably one is dead)

# Check update frequency
git log --all --pretty=format:"%h %ar %s" -- docs/README.md docs/DEPLOYMENT_GUIDE.md
```

### How to Fix

1. **Establish Single Authority**
   ```
   Choose: Which document is the source of truth?

   Option A: README.md is overview (entry point)
             DEPLOYMENT_GUIDE.md is detailed (reference)

   Option B: DEPLOYMENT_GUIDE.md is source
             README.md just links to it
   ```

2. **Remove Circular References**
   ```markdown
   # Good structure:

   README.md:
     "To deploy: See DEPLOYMENT_GUIDE.md"
     (just one link, no attempt to explain deployment)

   DEPLOYMENT_GUIDE.md:
     Full deployment instructions
     (no need to reference README)
   ```

3. **Update Ownership**
   ```bash
   # In CODEOWNERS or project docs:

   README.md: @team-leads
   DEPLOYMENT_GUIDE.md: @devops-team

   Schedule monthly reviews
   ```

### Checklist

- [ ] Identify which doc is authoritative
- [ ] Remove redundant explanations
- [ ] Use one-way links (source → dependent)
- [ ] Establish ownership for each doc
- [ ] Schedule regular reviews (monthly)
- [ ] Update timestamp on every review (even if no changes)

---

## Anti-Pattern 8: Implicit Contracts

**What It Is**: Component or API behavior that's "understood" but never documented.

**Why It's Bad**: Breaks **Principle 4** (Decisions are Explicit and Traceable). When someone asks "why does it work this way?" there's no answer.

### Example

```typescript
// Button component
export function Button({ variant, disabled, onClick }) {
  // What happens if someone clicks a disabled button?
  // Click handler fires? Doesn't fire? Returns early?
  // There's no documentation, so developers guess.
}
```

### Detection

```bash
# Look for undocumented behavior
grep -r "TODO\|FIXME\|XXX" code/src/ | grep -i "undocumented\|behavior\|need to"

# Check component files for missing JSDoc
find packages/components -name "*.tsx" -exec grep -L "@param\|@return" {} \;

# Check for implicit assumptions in tests
grep "it should" tests/*.test.ts | grep -v "it should.*{" | head -5
```

### How to Fix

1. **Document Component Contract**
   ```typescript
   /**
    * Button component
    *
    * @param variant - Button style (primary, secondary, ghost)
    * @param disabled - If true, button is not interactive (click ignored)
    * @param onClick - Called when button clicked (not called if disabled)
    *
    * Behavior:
    * - Disabled button: visual change (opacity 50%), click handler NOT invoked
    * - Icon + text: text displayed, icon as prefix
    */
   export function Button({ variant, disabled, onClick }) { ... }
   ```

2. **Document API Behavior**
   ```markdown
   # GET /api/tokens/{token_id}

   Returns token definition by ID.

   **Response (200):**
   { id: string, name: string, value: string }

   **Behavior:**
   - If token inherited from base theme, includes `inherits_from` field
   - If token has been overridden, includes `overridden_at` timestamp
   - Deleted tokens return 404

   **Not documented:** Old behavior returned 200 with deleted_at field
   Deprecated in v1.3.0, removed in v2.0.0
   ```

### Checklist

- [ ] Every component has JSDoc with @param, @return
- [ ] Every API endpoint documents behavior
- [ ] Error cases documented
- [ ] Edge cases documented
- [ ] Assumptions documented
- [ ] Links to related ADR if behavioral decision

---

## Learnings & Practices

### Learning 1: Versions Must Be Intentional

**The Hard Way:**
```
Team member updates API contract without bumping version.
External consumer pins to "latest" without specifying version.
Breaks. Consumer blames us. Trust eroded.
```

**The Right Way:**
```
Establish: Version bumps are code reviews (never silent)
External consumers: Always pin major version (>=1.0.0,<2.0.0)
Breaking changes: Announced 6 months before removal
```

### Learning 2: Duplicate Information Is a Technical Debt

**The Hard Way:**
```
API endpoint spec in 3 places:
- API_CONTRACTS.md
- README.md
- Integration guide

Someone fixes bug in one place.
Others become incorrect.
Different consumers see different specs.
```

**The Right Way:**
```
Spec lives in ONE place (API_CONTRACTS.md).
Other docs link to it.
Update once, everywhere is correct.
```

### Learning 3: Status Stales Quickly

**The Hard Way:**
```
"Last Updated: 2025-10-01"
It's now 2025-12-08 (2+ months old)
Is it still accurate?
Team doesn't know.
```

**The Right Way:**
```
Weekly status review (every Friday)
Automated reminders if stale
CI/CD fails if status older than 7 days
Before production deploy
```

### Learning 4: ADRs Aren't Just for Major Decisions

**The Hard Way:**
```
Year later: "Why do we cache tokens this way?"
Look at git history: "Cache tokens"
No context. No alternatives considered. No rationale.
Have to reverse-engineer decision.
```

**The Right Way:**
```
Even medium decisions get ADRs:
- Why caching (vs fetching each time)?
- Cache TTL strategy?
- What to do on cache miss?

Year later: Check ADR-NNN. All context there.
```

### Learning 5: Tools Are Better Than Endpoints for Agents

**The Hard Way:**
```
Agent has to:
1. Call HTTP endpoint (slow)
2. Parse HTTP response
3. Handle HTTP errors
4. Implement retry logic

Result: Agent can't work autonomously.
```

**The Right Way:**
```
Agent calls MCP tool directly:
1. Input validation built-in
2. Error handling built-in
3. Audit trail automatic
4. Agent can reason about operations directly

Result: Agent can work independently.
```

---

## When to Create ADRs vs Just Documenting

| Situation | ADR | Just Docs | Reason |
|-----------|-----|----------|--------|
| New endpoint added | Maybe | Yes (API_CONTRACTS.md) | Doc requirement, ADR only if trade-offs |
| Component design | Maybe | Yes (ARCHITECTURE.md) | Doc requirement, ADR if design pattern |
| Caching strategy | **Yes** | Plus docs | Affects performance, needs rationale |
| Bug fix | No | Maybe (PROJECT_STATUS.md) | Just update status if impactful |
| Token merge approach | **Yes** | Plus docs | Core architecture decision |
| Color palette | No | Yes (tokens/) | Design choice, not architectural |
| Authentication method | **Yes** | Plus docs | Security, backwards compatibility |
| URL structure | Maybe | Yes (API_CONTRACTS.md) | ADR only if non-obvious choice |

---

## Red Flags Checklist

When reviewing code, docs, or PRs, watch for:

- [ ] Hardcoded values (should be in config)
- [ ] Docstrings that contradict code
- [ ] Ghost scripts (unused but feared)
- [ ] Markdown files older than 30 days
- [ ] Technical specs in phase reports
- [ ] Version numbers that didn't change
- [ ] Circular documentation (A→B, B→A)
- [ ] Undocumented behavior
- [ ] "I think this is what it does" in comments
- [ ] PHASE_*.md files in root directory
- [ ] Different information in different docs

---

**Last Updated**: 2025-12-08
**Version**: 1.0.0 (extracted and expanded from PRINCIPLES.md)
**Status**: PRODUCTION
