Files
skills/solid/references/complexity.md
Mathias d6a71e370e
Some checks failed
release / tag (push) Has been cancelled
chore: bootstrap skills library — 19 skills + installer + CI auto-tag
Phase 1 of mathias/skills extraction (infra#62 Track D — homelab
next-step plan addendum). Imports ~/dev/.skills/ verbatim (19 skill
dirs + SKILLS_INDEX.md) and adds the installation surface:

- Taskfile.yml — install / update / list / release / check targets
- install.sh — bootstrap installer for hosts without Task. Idempotent
  symlink wirer; default checkout at ~/.local/share/skills/ on every
  host; SKILLS_REF env var pins a tag (default: main).
- .gitea/workflows/release.yml — auto-tag every push to main by
  Bump-Type footer (major/minor/patch, default patch). Skipped when
  commit contains [skip-release].
- README — usage, versioning, contribution flow, secret-hygiene rule.

Phase 1 wires Claude Code only (~/.claude/skills/<name> global +
<repo>/.claude/skills/<name> per-repo). Phase 2 adds Crush, opencode,
antigravity, and gitea-resident agents (cobalt-dingo, agentsquad)
once their skill conventions are researched.

Public repo, markdown-only — no secrets, no client names. Verified
via pre-push grep before initial push.

[skip-release]
2026-05-24 14:59:54 +02:00

287 lines
6.3 KiB
Markdown

# Managing Complexity
## The Two Types of Complexity
### Essential Complexity
Inherent to the problem domain. Cannot be removed, only managed.
- Business rules
- Domain logic
- User requirements
### Accidental Complexity
Introduced by our solutions. CAN and SHOULD be minimized.
- Poor abstractions
- Unnecessary indirection
- Framework ceremony
- Technical debt
**Goal: Minimize accidental complexity while clearly expressing essential complexity.**
---
## Detecting Complexity
### 1. Change Amplification
Small changes require touching many files.
**Symptom:** "To add this field, I need to update 15 files."
**Cause:** Scattered responsibilities, poor abstraction boundaries.
### 2. Cognitive Load
Code is hard to understand, requires holding too much in memory.
**Symptom:** "I need to understand 10 other classes to understand this one."
**Cause:** Tight coupling, hidden dependencies, unclear naming.
### 3. Unknown Unknowns
Behavior is surprising, side effects are hidden.
**Symptom:** "I changed this, and something completely unrelated broke."
**Cause:** Global state, hidden dependencies, implicit contracts.
---
## The XP Values for Fighting Complexity
From Extreme Programming:
### 1. Communication
Code should communicate clearly. Names, structure, tests all contribute.
### 2. Simplicity
Do the simplest thing that could possibly work.
### 3. Feedback
Fast feedback loops catch complexity early. TDD, CI, code review.
### 4. Courage
Refactor aggressively. Don't let complexity accumulate.
### 5. Respect
Respect future readers (including yourself). Write for humans first.
---
## KISS - Keep It Simple, Silly
> "The simplest solution that works is usually the best."
### How to Apply:
1. Start with the obvious solution
2. Only add complexity when REQUIRED
3. Prefer boring, well-understood approaches
4. Question every abstraction
```typescript
// Over-engineered
class UserServiceFactoryProvider {
private static instance: UserServiceFactoryProvider;
static getInstance(): UserServiceFactoryProvider { ... }
createFactory(): UserServiceFactory { ... }
}
// KISS
class UserService {
getUser(id: string): User { ... }
}
```
---
## YAGNI - You Aren't Gonna Need It
> "Don't build features until they're actually needed."
### Warning Signs:
- "We might need this later"
- "It would be nice to have"
- "Just in case"
- "For future extensibility"
### The Cost of YAGNI Violations:
1. **Development time** - Building unused features
2. **Maintenance burden** - Code that must be maintained
3. **Cognitive load** - More to understand
4. **Wrong abstraction** - Guessing future needs incorrectly
```typescript
// YAGNI violation: Building for hypothetical needs
class User {
// "We might need these someday"
middleName?: string;
secondaryEmail?: string;
faxNumber?: string;
linkedinProfile?: string;
twitterHandle?: string;
}
// YAGNI: Only what's needed NOW
class User {
name: string;
email: Email;
}
```
---
## DRY - Don't Repeat Yourself (with The Rule of Three)
> "Every piece of knowledge should have a single, unambiguous representation."
### BUT: The Rule of Three
**Don't extract duplication until you see it THREE times.**
Why? The wrong abstraction is worse than duplication.
```
Duplication #1 → Leave it
Duplication #2 → Note it, leave it
Duplication #3 → NOW extract it
```
### Example:
```typescript
// First time - leave it
function processUserOrder(order) {
validate(order);
calculateTax(order);
save(order);
}
// Second time - note the similarity, but leave it
function processGuestOrder(order) {
validate(order);
calculateTax(order);
save(order);
sendGuestEmail(order);
}
// Third time - NOW extract
function processCorporateOrder(order) {
validate(order);
calculateTax(order);
save(order);
applyCorporateDiscount(order);
}
// After three, extract the common parts
function processOrder(order: Order, postProcessing: (o: Order) => void) {
validate(order);
calculateTax(order);
save(order);
postProcessing(order);
}
```
---
## Separation of Concerns
> "Each module should address a single concern."
### Concerns to Separate:
- **Business logic** vs **Infrastructure**
- **What** (policy) vs **How** (mechanism)
- **Input** vs **Processing** vs **Output**
- **Data** vs **Behavior**
### Example:
```typescript
// BAD: Mixed concerns
class OrderProcessor {
process(order: Order) {
// Validation
if (!order.items.length) throw new Error('Empty');
// Business logic
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
// Persistence
const db = new Database();
db.query(`INSERT INTO orders...`);
// Notification
const email = new EmailClient();
email.send(order.customer.email, 'Order confirmed');
}
}
// GOOD: Separated concerns
class OrderProcessor {
constructor(
private validator: OrderValidator,
private calculator: OrderCalculator,
private repository: OrderRepository,
private notifier: OrderNotifier
) {}
process(order: Order): ProcessResult {
this.validator.validate(order);
const total = this.calculator.calculateTotal(order);
const savedOrder = this.repository.save(order);
this.notifier.notifyConfirmation(savedOrder);
return ProcessResult.success(savedOrder);
}
}
```
---
## Managing Technical Debt
### Types of Technical Debt:
1. **Deliberate** - Conscious trade-off for speed
2. **Accidental** - Mistakes, lack of knowledge
3. **Bit rot** - Code degrades over time
### The Boy Scout Rule:
> "Leave the code better than you found it."
Every time you touch code:
- Improve one small thing
- Fix one naming issue
- Extract one method
- Add one missing test
### When to Pay Down Debt:
- When it's in your path (you're already there)
- When it's blocking new features
- When it's causing bugs
- During dedicated refactoring time
### When NOT to Refactor:
- Code that works and won't change
- Code being replaced soon
- When you don't have tests
---
## The Four Elements of Simple Design
In priority order (from XP):
1. **Runs all the tests**
- If it doesn't work, nothing else matters
2. **Expresses intent**
- Clear names, obvious structure
- Code tells the story
3. **No duplication**
- DRY (but Rule of Three)
- Single source of truth
4. **Minimal**
- Fewest classes and methods possible
- Remove anything unnecessary
If these four are true, the design is simple enough.